From 4b0e7122eb56af68949022c21e17696fd22a129d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 18:09:31 +0000 Subject: [PATCH 01/35] Bump postcss from 8.5.6 to 8.5.10 in /docs/_preview Bumps [postcss](https://github.com/postcss/postcss) from 8.5.6 to 8.5.10. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.6...8.5.10) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.10 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- docs/_preview/package-lock.json | 9 +++++---- docs/_preview/package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/_preview/package-lock.json b/docs/_preview/package-lock.json index 248e35a432..9a5ffb8720 100644 --- a/docs/_preview/package-lock.json +++ b/docs/_preview/package-lock.json @@ -11,7 +11,7 @@ "devDependencies": { "@fullhuman/postcss-purgecss": "^7.0.2", "autoprefixer": "^10.4.22", - "postcss": "^8.5.6", + "postcss": "^8.5.10", "postcss-cli": "^11.0.1", "postcss-discard-comments": "^7.0.5" } @@ -785,9 +785,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "dev": true, "funding": [ { @@ -803,6 +803,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", diff --git a/docs/_preview/package.json b/docs/_preview/package.json index 143fef495c..92ad933edf 100644 --- a/docs/_preview/package.json +++ b/docs/_preview/package.json @@ -11,7 +11,7 @@ "devDependencies": { "@fullhuman/postcss-purgecss": "^7.0.2", "autoprefixer": "^10.4.22", - "postcss": "^8.5.6", + "postcss": "^8.5.10", "postcss-cli": "^11.0.1", "postcss-discard-comments": "^7.0.5" } From 89f91427ff8769cf8461e3e60e0401be54b92287 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 29 Apr 2026 20:07:58 +0100 Subject: [PATCH 02/35] Bump version -> `2.0.0-SNAPSHOT.415` --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index f7cddf7dd1..2e5fcb8bd6 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -27,4 +27,4 @@ /** * The version of the Validation SDK to publish. */ -val validationVersion by extra("2.0.0-SNAPSHOT.414") +val validationVersion by extra("2.0.0-SNAPSHOT.415") From 113f908e8ee794e627ffcab54a408b536b85f052 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 29 Apr 2026 20:10:47 +0100 Subject: [PATCH 03/35] Bump local dependencies --- .../main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt | 4 ++-- buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt | 2 +- .../src/main/kotlin/io/spine/dependency/local/Validation.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index dfdaf95357..0a52ff7826 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -46,12 +46,12 @@ object CoreJvmCompiler { /** * The version used to in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.062" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.063" /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.062" + const val version = "2.0.0-SNAPSHOT.063" /** * The ID of the Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt index e78af421da..2de2af2acf 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt @@ -40,7 +40,7 @@ import io.spine.dependency.Dependency ) object Time : Dependency() { override val group = Spine.group - override val version = "2.0.0-SNAPSHOT.235" + override val version = "2.0.0-SNAPSHOT.236" private const val infix = "spine-time" fun lib(version: String): String = "$group:$infix:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt index a5ef403185..121d7aac24 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt @@ -36,7 +36,7 @@ object Validation { /** * The version of the Validation library artifacts. */ - const val version = "2.0.0-SNAPSHOT.413" + const val version = "2.0.0-SNAPSHOT.414" /** * The last version of Validation compatible with ProtoData. From 6b2bda4a1198eb8b5ca9e628bec23cb23e62f6a7 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 29 Apr 2026 20:10:55 +0100 Subject: [PATCH 04/35] Update dependency reports --- dependencies.md | 60 ++++++++++++++++++++++++------------------------- pom.xml | 15 ++++++++----- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/dependencies.md b/dependencies.md index 497e7fddf8..39d714342d 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1090,14 +1090,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1791,28 +1791,28 @@ This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.412` +# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.415` ## Runtime ## Compile, tests, and tooling The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Apr 24 19:11:43 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -2864,14 +2864,14 @@ This report was generated on **Fri Apr 24 19:11:43 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -3961,14 +3961,14 @@ This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. @@ -4015,14 +4015,14 @@ This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:16 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:spine-validation-jvm-runtime:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine:spine-validation-jvm-runtime:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -4822,14 +4822,14 @@ This report was generated on **Wed Apr 29 15:56:16 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -5511,14 +5511,14 @@ This report was generated on **Wed Apr 29 15:56:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -5976,14 +5976,14 @@ This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -6602,14 +6602,14 @@ This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -7170,14 +7170,14 @@ This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -7781,14 +7781,14 @@ This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -8526,14 +8526,14 @@ This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8766,14 +8766,14 @@ This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.414` +# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.415` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -9116,6 +9116,6 @@ This report was generated on **Wed Apr 29 15:56:18 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 15:56:17 WEST 2026** using +This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0e1aa88d5c..743612140c 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools validation -2.0.0-SNAPSHOT.414 +2.0.0-SNAPSHOT.415 2015 @@ -56,13 +56,13 @@ all modules and does not describe the project structure per-subproject. io.spine spine-time - 2.0.0-SNAPSHOT.235 + 2.0.0-SNAPSHOT.236 compile io.spine spine-validation-jvm-runtime - 2.0.0-SNAPSHOT.411 + 2.0.0-SNAPSHOT.414 compile @@ -269,7 +269,7 @@ all modules and does not describe the project structure per-subproject. io.spine.tools core-jvm-plugins - 2.0.0-SNAPSHOT.062 + 2.0.0-SNAPSHOT.063 io.spine.tools @@ -281,10 +281,15 @@ all modules and does not describe the project structure per-subproject. spine-dokka-extensions 2.0.0-SNAPSHOT.7 + + io.spine.tools + time-validation + 2.0.0-SNAPSHOT.236 + io.spine.tools validation-java-bundle - 2.0.0-SNAPSHOT.411 + 2.0.0-SNAPSHOT.414 net.sourceforge.pmd From 1f5552fad6849435bfed06e36517a26c59323584 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 29 Apr 2026 20:12:34 +0100 Subject: [PATCH 05/35] Bump Validation in docs --- docs/_examples | 2 +- .../docs/validation/01-getting-started/adding-to-build.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/_examples b/docs/_examples index b75f86539e..bc2c4a547f 160000 --- a/docs/_examples +++ b/docs/_examples @@ -1 +1 @@ -Subproject commit b75f86539e9f6a8d4512ccc14e4be28bc5b97492 +Subproject commit bc2c4a547fa600e01a1f67c0529c18c9d81939f4 diff --git a/docs/content/docs/validation/01-getting-started/adding-to-build.md b/docs/content/docs/validation/01-getting-started/adding-to-build.md index 2d65b5d8f0..1a461a8636 100644 --- a/docs/content/docs/validation/01-getting-started/adding-to-build.md +++ b/docs/content/docs/validation/01-getting-started/adding-to-build.md @@ -90,7 +90,7 @@ Add the Validation plugin to the build. ```kotlin plugins { module - id("io.spine.validation") version "2.0.0-SNAPSHOT.413" + id("io.spine.validation") version "2.0.0-SNAPSHOT.415" } ``` @@ -120,7 +120,7 @@ adding Validation directly. CoreJvm brings in the Validation Gradle plugin for y ```kotlin plugins { module - id("io.spine.core-jvm") version "2.0.0-SNAPSHOT.062" + id("io.spine.core-jvm") version "2.0.0-SNAPSHOT.063" } ``` From f554bd5ff0b0c6e38b1a043dd2be8e40e76f800d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 29 Apr 2026 20:12:55 +0100 Subject: [PATCH 06/35] Update `config` --- .gitignore | 1 + .junie/guidelines.md | 2 +- buildSrc/build.gradle.kts | 2 -- buildSrc/src/main/kotlin/io/spine/dependency/build/Dokka.kt | 1 - .../kotlin/io/spine/dependency/local/CoreJvmCompiler.kt | 2 +- buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt | 6 ++++++ config | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index a3e0ec13bb..5f85d295e8 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ # Internal tool directories. .fleet/ +.junie/memory/ # Kotlin temp directories. **/.kotlin/ diff --git a/.junie/guidelines.md b/.junie/guidelines.md index 459e28ecae..5160f499e6 100644 --- a/.junie/guidelines.md +++ b/.junie/guidelines.md @@ -10,7 +10,7 @@ Also follow the Junie-specific rules described below. ## Junie Assistance Tips -When working with Junie AI on the Spine Tool-Base project: +When working with Junie AI on the Spine family of projects: 1. **Project Navigation**: Use `search_project` to find relevant files and code segments. 2. **Code Understanding**: Request file structure with `get_file_structure` before editing. diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 6496c646c3..4184e9bf2d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -137,8 +137,6 @@ val koverVersion = "0.9.1" /** * The version of the Shadow Plugin. * - * `7.1.2` is the last version compatible with Gradle 7.x. Newer versions require Gradle v8.x. - * * @see Shadow Plugin releases */ val shadowVersion = "9.4.1" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/build/Dokka.kt b/buildSrc/src/main/kotlin/io/spine/dependency/build/Dokka.kt index 858731b3fc..6b4822dcd0 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/build/Dokka.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/build/Dokka.kt @@ -26,7 +26,6 @@ package io.spine.dependency.build -import io.spine.dependency.build.Dokka.GradlePlugin.id import io.spine.dependency.local.Spine // https://github.com/Kotlin/dokka diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 0a52ff7826..2333e4ce7e 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -51,7 +51,7 @@ object CoreJvmCompiler { /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.063" + const val version = "2.0.0-SNAPSHOT.062" /** * The ID of the Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt index 2de2af2acf..05eb7bfaf0 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt @@ -56,6 +56,12 @@ object Time : Dependency() { fun testLib(version: String): String = "${Spine.toolsGroup}:time-testlib:$version" val testLib get() = testLib(version) + fun validation(version: String): String = "${Spine.toolsGroup}:time-validation:$version" + val validation get() = validation(version) + + fun gradlePlugin(version: String): String = "${Spine.toolsGroup}:time-gradle-plugin:$version" + val gradlePlugin get() = gradlePlugin(version) + override val modules: List get() = listOf( lib, diff --git a/config b/config index 8d540c50a6..5118c00783 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 8d540c50a6ecb118c8fd1080b45054c0a3909ea9 +Subproject commit 5118c0078339c7fe6d74d652921c42010cf64a37 From a8895239cb08b22c51b8671752256e22cf12c034 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 29 Apr 2026 20:27:14 +0100 Subject: [PATCH 07/35] Add Time as a submodule under `docs/_time` --- .gitmodules | 3 +++ docs/_time | 1 + 2 files changed, 4 insertions(+) create mode 160000 docs/_time diff --git a/.gitmodules b/.gitmodules index 97a9d16eb5..c9606ebb47 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "docs/_examples"] path = docs/_examples url = https://github.com/spine-examples/hello-validation +[submodule "docs/_time"] + path = docs/_time + url = https://github.com/SpineEventEngine/time diff --git a/docs/_time b/docs/_time new file mode 160000 index 0000000000..ff52ea3322 --- /dev/null +++ b/docs/_time @@ -0,0 +1 @@ +Subproject commit ff52ea332267a8f912c3d19c94b0bd5a37d00667 From 646376a83c82ec19b6e697e5ff98c6246bb211df Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 16:34:56 +0100 Subject: [PATCH 08/35] Update section number --- .../validation/01-getting-started/generated-code.md | 2 +- docs/content/docs/validation/02-concepts/_index.md | 4 ++-- .../docs/validation/02-concepts/error-messages.md | 2 +- .../docs/validation/02-concepts/options-overview.md | 2 +- .../docs/validation/03-built-in-options/_index.md | 2 +- .../content/docs/validation/04-validators/_index.md | 4 ++-- .../04-validators/implement-a-validator.md | 2 +- .../_index.md | 0 .../typical_custom_option.jpg | Bin .../validation/09-developers-guide/architecture.md | 2 +- .../validation/09-developers-guide/key-modules.md | 2 +- docs/content/docs/validation/_index.md | 2 +- .../data/docs/validation/2-0-0-snapshot/sidenav.yml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) rename docs/content/docs/validation/{08-custom-validation => 05-custom-validation}/_index.md (100%) rename docs/content/docs/validation/{08-custom-validation => 05-custom-validation}/typical_custom_option.jpg (100%) diff --git a/docs/content/docs/validation/01-getting-started/generated-code.md b/docs/content/docs/validation/01-getting-started/generated-code.md index ce767b0786..40ba676c1f 100644 --- a/docs/content/docs/validation/01-getting-started/generated-code.md +++ b/docs/content/docs/validation/01-getting-started/generated-code.md @@ -110,4 +110,4 @@ such as API requests or deserialized data, for example, when building an anticor - Learn the core concepts: [Concepts](../02-concepts/). - If you need organization-specific rules: - [Custom validation](../08-custom-validation/). + [Custom validation](../05-custom-validation/). diff --git a/docs/content/docs/validation/02-concepts/_index.md b/docs/content/docs/validation/02-concepts/_index.md index 09d8628ea5..de85bc608b 100644 --- a/docs/content/docs/validation/02-concepts/_index.md +++ b/docs/content/docs/validation/02-concepts/_index.md @@ -91,7 +91,7 @@ which message was validated at the top level. If built-in options are not enough, you can add organization-specific options and generate code for them. -See [Custom validation](../08-custom-validation/) for the workflow and a reference example. +See [Custom validation](../05-custom-validation/) for the workflow and a reference example. ## What’s next @@ -107,4 +107,4 @@ See [Custom validation](../08-custom-validation/) for the workflow and a referen - [Built-in options](../03-built-in-options/) - [Using validators](../04-validators/) - Add custom validation options: - [Custom validation](../08-custom-validation/). + [Custom validation](../05-custom-validation/). diff --git a/docs/content/docs/validation/02-concepts/error-messages.md b/docs/content/docs/validation/02-concepts/error-messages.md index 4a78f893b1..b573bb98d3 100644 --- a/docs/content/docs/validation/02-concepts/error-messages.md +++ b/docs/content/docs/validation/02-concepts/error-messages.md @@ -139,4 +139,4 @@ Recommended actions: - Learn how to validate messages with custom code: [Using validators](../04-validators/). - If built-in options are not enough, define your own constraints and messages: - [Custom validation](../08-custom-validation/). + [Custom validation](../05-custom-validation/). diff --git a/docs/content/docs/validation/02-concepts/options-overview.md b/docs/content/docs/validation/02-concepts/options-overview.md index a304cca991..11f54d07c2 100644 --- a/docs/content/docs/validation/02-concepts/options-overview.md +++ b/docs/content/docs/validation/02-concepts/options-overview.md @@ -111,4 +111,4 @@ message Temperature { ## What’s next - [Built-in options](../03-built-in-options/) -- [Custom validation](../08-custom-validation/) +- [Custom validation](../05-custom-validation/) diff --git a/docs/content/docs/validation/03-built-in-options/_index.md b/docs/content/docs/validation/03-built-in-options/_index.md index 04356e24de..45d4096550 100644 --- a/docs/content/docs/validation/03-built-in-options/_index.md +++ b/docs/content/docs/validation/03-built-in-options/_index.md @@ -47,4 +47,4 @@ on GitHub: [spine/options.proto](https://github.com/SpineEventEngine/base-librar - [Message-level options](message-level-options.md) - [Options for `repeated` and `map` fields](repeated-and-map-fields.md) - [Using validators](../04-validators/) -- [Custom validation](../08-custom-validation/) +- [Custom validation](../05-custom-validation/) diff --git a/docs/content/docs/validation/04-validators/_index.md b/docs/content/docs/validation/04-validators/_index.md index e7bb17d412..e6969bc49a 100644 --- a/docs/content/docs/validation/04-validators/_index.md +++ b/docs/content/docs/validation/04-validators/_index.md @@ -26,7 +26,7 @@ Validators are implemented via `io.spine.validation.MessageValidator` and exe Prefer `.proto` options when you can: 1. Use [built-in options](../03-built-in-options/). -2. If built-ins are not enough, implement [custom validation options](../08-custom-validation/). +2. If built-ins are not enough, implement [custom validation options](../05-custom-validation/). Use `MessageValidator` when: @@ -107,7 +107,7 @@ their violations together. ## What’s next - [Implement a validator](implement-a-validator.md) - [Using `ValidatorRegistry`](validator-registry.md) -- [Custom validation](../08-custom-validation/) +- [Custom validation](../05-custom-validation/) - [Architecture](../09-developers-guide/architecture.md) [auto-service]: https://github.com/google/auto/tree/main/service diff --git a/docs/content/docs/validation/04-validators/implement-a-validator.md b/docs/content/docs/validation/04-validators/implement-a-validator.md index 0ae1f65e3b..a2f33f235c 100644 --- a/docs/content/docs/validation/04-validators/implement-a-validator.md +++ b/docs/content/docs/validation/04-validators/implement-a-validator.md @@ -136,7 +136,7 @@ to the nested field in error (for example, to `starts_at.seconds`). ## What’s next - [Using `ValidatorRegistry`](validator-registry.md) -- [Custom validation](../08-custom-validation/) +- [Custom validation](../05-custom-validation/) - [Architecture](../09-developers-guide/architecture.md) diff --git a/docs/content/docs/validation/08-custom-validation/_index.md b/docs/content/docs/validation/05-custom-validation/_index.md similarity index 100% rename from docs/content/docs/validation/08-custom-validation/_index.md rename to docs/content/docs/validation/05-custom-validation/_index.md diff --git a/docs/content/docs/validation/08-custom-validation/typical_custom_option.jpg b/docs/content/docs/validation/05-custom-validation/typical_custom_option.jpg similarity index 100% rename from docs/content/docs/validation/08-custom-validation/typical_custom_option.jpg rename to docs/content/docs/validation/05-custom-validation/typical_custom_option.jpg diff --git a/docs/content/docs/validation/09-developers-guide/architecture.md b/docs/content/docs/validation/09-developers-guide/architecture.md index 1ba2c01559..0a4240b081 100644 --- a/docs/content/docs/validation/09-developers-guide/architecture.md +++ b/docs/content/docs/validation/09-developers-guide/architecture.md @@ -24,4 +24,4 @@ The workflow is the following: ## What’s next - See the project layout: [Key modules](key-modules.md). -- If you need organization-specific rules: [Custom validation](../08-custom-validation/). +- If you need organization-specific rules: [Custom validation](../05-custom-validation/). diff --git a/docs/content/docs/validation/09-developers-guide/key-modules.md b/docs/content/docs/validation/09-developers-guide/key-modules.md index 6b13444c51..38fd4a20a9 100644 --- a/docs/content/docs/validation/09-developers-guide/key-modules.md +++ b/docs/content/docs/validation/09-developers-guide/key-modules.md @@ -38,4 +38,4 @@ project paths (e.g. `:java`, `:tests:vanilla`). ## What’s next -- Build custom validation rules: [Custom validation](../08-custom-validation/). +- Build custom validation rules: [Custom validation](../05-custom-validation/). diff --git a/docs/content/docs/validation/_index.md b/docs/content/docs/validation/_index.md index befa11289e..6082e60873 100644 --- a/docs/content/docs/validation/_index.md +++ b/docs/content/docs/validation/_index.md @@ -19,4 +19,4 @@ options, and runs those checks automatically when you build messages. - [Built-in options](03-built-in-options/) - [Using validators](04-validators/) - How it works: [Architecture](09-developers-guide/architecture.md) -- Extension points: [Custom validation](08-custom-validation/) +- Extension points: [Custom validation](05-custom-validation/) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index ac1887e642..3a801811e3 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -58,7 +58,7 @@ - page: Using `ValidatorRegistry` file_path: 04-validators/validator-registry - page: Custom validation - file_path: 08-custom-validation + file_path: 05-custom-validation - page: Developer’s guide key: 09-developers-guide children: From 0ec7d7bed16112f1ff45ce7646d677ee9e46b1e2 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 17:02:29 +0100 Subject: [PATCH 09/35] Compose the plan for describing custom validation options --- .../tasks/extend-custom-validation-docs.md | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 .agents/tasks/extend-custom-validation-docs.md diff --git a/.agents/tasks/extend-custom-validation-docs.md b/.agents/tasks/extend-custom-validation-docs.md new file mode 100644 index 0000000000..b32b74088d --- /dev/null +++ b/.agents/tasks/extend-custom-validation-docs.md @@ -0,0 +1,253 @@ +# Task: Extend "Custom validation" documentation + +## Goal + +Extend `docs/content/docs/validation/05-custom-validation/_index.md` with step-by-step +guidance on implementing a custom validation option and its code generator. The existing +content provides a correct high-level overview; the task is to add concrete detail and +working code examples derived from the `(when)` option implementation in +`docs/_time/validation/`. + +**Target audience**: a developer integrating a new custom option into an existing +Spine-based build plugin. + +**Acceptance criteria**: after reading the extended section, a developer can implement all +required artefacts (proto, Kotlin classes, `@AutoService` registrations) for a new +field-level option without consulting the source code. + +--- + +## Source references + +All code examples should be verified against and derived from these files: + +| Artefact | Path | +|---|---| +| Proto option definition | `docs/_time/time/src/main/proto/spine/time_options.proto` | +| Event proto message | `docs/_time/validation/src/main/proto/spine/tools/time/validation/events.proto` | +| View state proto message | `docs/_time/validation/src/main/proto/spine/tools/time/validation/views.proto` | +| `ValidationOption` + `Reaction` + `View` | `docs/_time/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenOption.kt` | +| `OptionGenerator` | `docs/_time/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt` | +| `OptionsProvider` registration | `tests/extensions/src/main/kotlin/io/spine/tools/validation/test/CustomOptionsProvider.kt` | +| `ValidationOption` SPI entry-point | `tests/extensions/src/main/kotlin/io/spine/tools/validation/test/CurrencyOption.kt` | + +--- + +## Gaps in current documentation + +The current `_index.md` is missing: + +1. **Proto artefacts** — no explanation of the three distinct proto definitions required: + - the option message itself (e.g. `TimeOption` + `Time` enum, extension field), + - the domain event emitted by the reaction (e.g. `WhenFieldDiscovered`), + - the view state accumulator (e.g. `WhenField` with `(entity).kind = PROJECTION`). +2. **`OptionsProvider` registration** — mentioned in step 2 but no code shown. +3. **`ValidationOption` entry-point** — no code showing the `@AutoService` annotation, + the `NAME` constant convention, or how reactions/view/generator are wired together. +4. **Reaction** — does not explain the `@Where` filter, the `EitherOf2` + return type, the "disabled option" short-circuit (e.g. `TIME_UNDEFINED`), or the + `checkPlaceholders` / `Compilation.check` error-reporting API. +5. **View** — does not show the proto backing message, the `FieldRef` identity pattern, or + the `alter { }` DSL. +6. **Generator** — does not explain `OptionGeneratorWithConverter`, lazy view querying, or + `SingleOptionCode`. + +--- + +## File structure + +Mirror the convention used in `docs/content/docs/validation/04-validators/`: +the `_index.md` provides the overview and "What's next" navigation; each detailed +topic lives in its own page. + +``` +docs/content/docs/validation/05-custom-validation/ + _index.md ← existing file, update only + declare-the-option.md ← new + register-the-option.md ← new + implement-the-reaction.md ← new + implement-the-view.md ← new + implement-the-generator.md ← new +``` + +--- + +## Changes to `_index.md` + +- Keep the existing high-level 4-step overview and the workflow diagram unchanged. +- Replace the detailed prose sub-sections ("Reaction", "View", "Generator") with a + short one-liner each that links to the corresponding new page. +- Update the "What's next" block to include links to all five new pages. + +--- + +## Page specifications + +### `declare-the-option.md` + +**Front-matter title**: `Declare the option in Protobuf` + +Three sub-sections, each with a prose introduction and a code snippet. + +#### Declare the option message + +- Explain that the option is a Protobuf `extend` block targeting one of the standard + descriptor option types (`FieldOptions`, `MessageOptions`, etc.). +- Show a trimmed version of `time_options.proto`: + - `extend google.protobuf.FieldOptions { TimeOption when = 73819; }` + - the `TimeOption` message with `Time in = 1` and `string error_msg = 3` + - the `Time` enum (`TIME_UNDEFINED`, `PAST`, `FUTURE`) + - the `(default_message)` annotation on `TimeOption` +- Note: omit `package` from the proto file if shorter option usage in `.proto` files is + desired (cite the comment in `time_options.proto` that explains this trade-off). + +#### Declare the event + +- Explain that the Reaction emits a domain event that carries all data the View and + Generator need. +- Show a trimmed version of `events.proto`: + - `message WhenFieldDiscovered` with `compiler.FieldRef id = 1`, `compiler.Field subject`, + `string error_message`, `Time bound`, `TimeFieldType type`. +- Note: `id` must be the first field and must be the same type that the View uses as its + identity (`FieldRef` in this case). + +#### Declare the view state + +- Explain that the view state is the persistent accumulator queried by the Generator. +- Show a trimmed version of `views.proto`: + - `message WhenField` with `option (entity).kind = PROJECTION` and the same fields as + the event. +- Explain that the `id` field must match the event `id` type so the framework routes the + event to the correct view instance. + +--- + +### `register-the-option.md` + +**Front-matter title**: `Register the option` + +Two sub-sections. + +#### Register the proto extension + +- Show the `@AutoService(OptionsProvider::class)` pattern from `CustomOptionsProvider.kt` + (call `registerAllExtensions` on the generated outer class of the proto file containing + the `extend` block). +- Explain that this makes the option visible to the Protobuf descriptor machinery at + runtime. + +#### Wire up ValidationOption + +- Show the full `WhenOption` class (public API only, omit private helpers): + - `@AutoService(ValidationOption::class)` annotation + - `companion object { const val NAME = "when" }` — explain this string is the value + matched by `@Where` in the Reaction. + - `override val reactions`, `override val view`, `override val generator` +- Note: one `ValidationOption` per custom option; only one generator is allowed per + `ValidationOption`. + +--- + +### `implement-the-reaction.md` + +**Front-matter title**: `Implement the Reaction` + +- Show the class declaration: `internal class WhenReaction : Reaction()`. +- Show the reaction method signature with its annotations: + ```kotlin + @React + override fun whenever( + @External @Where(field = OPTION_NAME, equals = WhenOption.NAME) + event: FieldOptionDiscovered + ): EitherOf2 + ``` +- Explain each annotation: + - `@External` — the event originates from the compiler's bounded context. + - `@Where` — narrows the subscription to events for this option only; `OPTION_NAME` is + a constant from `io.spine.tools.validation`. +- Describe the three possible outcomes: + 1. **Unsupported field type** — call `Compilation.check(...)` to emit a compile error + and stop processing. + 2. **Disabled option** — short-circuit with `return ignore()` (emits `NoReaction`) when + the option value is the sentinel `TIME_UNDEFINED`. + 3. **Valid, enabled option** — validate the error message template via + `checkPlaceholders(SUPPORTED_PLACEHOLDERS, field, file, WhenOption.NAME)`, then build + and return the domain event using the Kotlin DSL + (`whenFieldDiscovered { ... }.asA()`). +- Include a concise end-to-end code snippet of the `WhenReaction.whenever()` body. + +--- + +### `implement-the-view.md` + +**Front-matter title**: `Implement the View` + +- Show the class declaration: + `internal class WhenFieldView : View()` +- Explain the three type parameters: ID type, state proto message, state builder. +- Show the subscriber method: + ```kotlin + @Subscribe + fun on(e: WhenFieldDiscovered) = alter { + subject = e.subject + errorMessage = e.errorMessage + bound = e.bound + type = e.type + } + ``` +- Note: views only accumulate data; validation and business logic belong in the Reaction. + +--- + +### `implement-the-generator.md` + +**Front-matter title**: `Implement the Generator` + +- Show the class declaration: + `internal class WhenGenerator : OptionGeneratorWithConverter()` +- Explain when to use `OptionGeneratorWithConverter` vs the plain `OptionGenerator` base + (the former injects a `JavaValueConverter` for converting proto field values to Java + code expressions). +- Show the lazy view query: + ```kotlin + private val allWhenFields by lazy { + querying.select().all() + } + ``` + Explain that `querying` is injected by the framework and must not be accessed before + the generator is invoked. +- Show the `codeFor` override: + ```kotlin + override fun codeFor(type: TypeName): List = + allWhenFields + .filter { it.id.type == type } + .map { GenerateWhen(it, converter).code() } + ``` + Explain that the framework calls `codeFor` once per processed message type and that + each returned `SingleOptionCode` wraps a `CodeBlock` inlined into the generated + `validate()` method. +- Recommend the inner-class pattern (`GenerateWhen`) to separate "gather view data" + (generator) from "compose code string" (inner class), but note that internal helpers + can be omitted from documentation for brevity. + +--- + +## Style and formatting rules + +- Each new page must include a YAML front-matter block with `title`, `description`, and + `headline: Documentation` (follow existing pages in `04-validators/`). +- Use `##` for top-level sections and `###` for sub-sections within a page. +- Use fenced code blocks with language tags (`kotlin`, `protobuf`). +- Keep prose paragraphs short (≤4 lines); use bullet lists for multi-item enumerations. +- Avoid widows, runts, orphans, and rivers (see `.agents/documentation-guidelines.md`). +- All class/method/field names must match the actual source identifiers exactly. +- End each page with a "What's next" section linking forward to the next page and back + to the section index (`../`). + +--- + +## Out of scope + +- End-to-end code for a hypothetical new option. +- Documenting the `currency` option separately (it is already referenced in `_index.md`). +- Changes to any file outside `docs/content/docs/validation/05-custom-validation/`. From 1913270e0b2cf7a3607e1147925c4f038c1ffe10 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 17:25:36 +0100 Subject: [PATCH 10/35] Draft the seacion "Custom validation option" --- .../validation/05-custom-validation/_index.md | 81 ++++-------- .../declare-the-option.md | 110 ++++++++++++++++ .../implement-the-generator.md | 66 ++++++++++ .../implement-the-reaction.md | 122 ++++++++++++++++++ .../implement-the-view.md | 49 +++++++ .../register-the-option.md | 81 ++++++++++++ 6 files changed, 455 insertions(+), 54 deletions(-) create mode 100644 docs/content/docs/validation/05-custom-validation/declare-the-option.md create mode 100644 docs/content/docs/validation/05-custom-validation/implement-the-generator.md create mode 100644 docs/content/docs/validation/05-custom-validation/implement-the-reaction.md create mode 100644 docs/content/docs/validation/05-custom-validation/implement-the-view.md create mode 100644 docs/content/docs/validation/05-custom-validation/register-the-option.md diff --git a/docs/content/docs/validation/05-custom-validation/_index.md b/docs/content/docs/validation/05-custom-validation/_index.md index d207d6a7b6..7c4086e3c5 100644 --- a/docs/content/docs/validation/05-custom-validation/_index.md +++ b/docs/content/docs/validation/05-custom-validation/_index.md @@ -10,73 +10,46 @@ Users can extend the library by providing custom Protobuf options and code gener Follow these steps to create a custom option: -1. Declare a Protobuf [extension](https://protobuf.dev/programming-guides/proto3/#customoptions) - in your `.proto` file. -2. Register it via `io.spine.option.OptionsProvider`. -3. Implement the following entities: - - Reaction (`MyOptionReaction`) – discovers and validates the option. - - View (`MyOptionView`) – accumulates valid option applications. - - Generator (`MyOptionGenerator`) – generates Java code for the option. -4. Register them via `io.spine.tools.validation.java.ValidationOption`. +1. [Declare](declare-the-option.md) a Protobuf extension, a domain event, and a view state in + your `.proto` files. +2. [Register](register-the-option.md) the Protobuf extension via `io.spine.option.OptionsProvider` + and wire up the entities via `io.spine.tools.validation.java.ValidationOption`. +3. [Implement the Reaction](implement-the-reaction.md) — discovers and validates the option. +4. [Implement the View](implement-the-view.md) — accumulates valid option applications. +5. [Implement the Generator](implement-the-generator.md) — generates Java code for the option. Below is a workflow diagram for a typical option: ![Typical custom option](typical_custom_option.jpg) -## What’s next - -- [Using validators](../04-validators/) -- Learn where this plugs in: [Architecture](../09-developers-guide/architecture.md). - -Take a look at the `:tests:extensions` module that contains a full example of -implementation of the custom `(currency)` option. +Note that a custom option can provide several reactions and views, but only one generator. +This allows building more complex models using more entities and events. -Note that a custom option can provide several policies and views, but only one generator. -This allows building more complex models, using more entities and events. - -Let's take a closer look at each entity. +## Components ### Reaction -Usually, this is an entry point to the option handling. - -The reaction subscribes to one of `*OptionDiscovered` events: - -- `FileOptionDiscovered`. -- `MessageOptionDiscovered`. -- `FieldOptionDiscovered`. -- `OneofOptionDiscovered`. - -It filters incoming events, taking only those who contain the option of the interest. The reaction -may validate the option application, query `TypeSystem`, extract and transform data arrived with -the option, if any. Once ready, it emits an event signaling that the discovered option is valid -and ready for the code generation. - -The reaction may report a compilation warning or an error, failing the whole compilation if it -finds an illegal application of the option. - -For example: - -1. An unsupported field type. -2. Illegal option content (invalid regex, parameter, signature). - -The reaction may just ignore the discovered option and emit `NoReaction`. A typical example -of this is a boolean option, such as `(required)`, which does nothing when it is set to `false`. - -The desired behavior depends on the option itself. +Subscribes to `*OptionDiscovered` events and filters them by option name. +See [Implement the Reaction](implement-the-reaction.md) for details. ### View -Views accumulate events from policies, serving as data providers for the validation model -used by code generators. Views are typically simple and only accumulate data; for more complex -logic, use policies. - -Usually, one view represents a single application of an option. +Accumulates events emitted by the Reaction so the Generator can query them. +See [Implement the View](implement-the-view.md) for details. ### Generator -The generator is an entity that provides an actual implementation of the option behavior. -The generator produces Java code for every application of that option within the message type. +Produces Java code for every application of the option within a message type. +See [Implement the Generator](implement-the-generator.md) for details. + +## What’s next + +- [Declare the option in Protobuf](declare-the-option.md) +- [Register the option](register-the-option.md) +- [Implement the Reaction](implement-the-reaction.md) +- [Implement the View](implement-the-view.md) +- [Implement the Generator](implement-the-generator.md) +- [Using validators](../04-validators/) +- Learn where this plugs in: [Architecture](../09-developers-guide/architecture.md). -It has access to the `Querying` interface and can query views to find those belonging -to the processed message type. +The `:tests:extensions` module contains a full example of the custom `(currency)` option. diff --git a/docs/content/docs/validation/05-custom-validation/declare-the-option.md b/docs/content/docs/validation/05-custom-validation/declare-the-option.md new file mode 100644 index 0000000000..d6520461b1 --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/declare-the-option.md @@ -0,0 +1,110 @@ +--- +title: Declare the option in Protobuf +description: How to define the option message, domain event, and view state in Protobuf. +headline: Documentation +--- + +# Declare the option in Protobuf + +A custom validation option requires three distinct Protobuf definitions: + +1. The option message itself (extends a standard descriptor option type). +2. A domain event emitted by the Reaction when a valid option application is found. +3. A view state that persists the event data for the Generator to query. + +The examples below are taken from the `(when)` option in `docs/_time/`. + +## Declare the option message + +An option is declared as a Protobuf `extend` block targeting one of the standard +descriptor option types: `google.protobuf.FieldOptions`, `MessageOptions`, and so on. + +```protobuf +extend google.protobuf.FieldOptions { + TimeOption when = 73819; +} + +message TimeOption { + + option (default_message) = "The field `${parent.type}.${field.path}`" + " of the type `${field.type}` must be in the `${when.in}`." + " The encountered value: `${field.value}`."; + + Time in = 1; + + // field 2 is reserved (deprecated msg_format). + + // A user-defined error message. + string error_msg = 3; +} + +enum Time { + TIME_UNDEFINED = 0; + PAST = 1; + FUTURE = 2; +} +``` + +The `(default_message)` option on `TimeOption` sets the error template used when the caller +does not supply a custom `error_msg`. Field number `73819` is the globally registered extension +number for this option; every extension must have a unique number in the allowed range. + +### Packaging trade-off + +If you omit the `package` declaration from the `.proto` file that defines the extension, callers +can write `[(when).in = FUTURE]` instead of `[(spine.time.when).in = FUTURE]`. This is a +deliberate trade-off: shorter option syntax at the cost of no package-level namespacing. The +`time_options.proto` file explains this choice in its header comment. + +## Declare the event + +The Reaction emits a domain event carrying all data that the View and Generator need. The event +travels through the compilation bounded context, so it must be a proper Protobuf message. + +```protobuf +message WhenFieldDiscovered { + + compiler.FieldRef id = 1; + + compiler.Field subject = 2; + + string error_message = 3; + + Time bound = 4; + + spine.tools.time.validation.TimeFieldType type = 5; +} +``` + +The `id` field must be the **first** field and must be the same type that the View uses as its +entity identity (`compiler.FieldRef` in this case). The framework uses the identity field to +route the event to the correct View instance. + +## Declare the view state + +The view state is the persistent accumulator queried by the Generator. It mirrors the event +fields and is marked as a Spine projection: + +```protobuf +message WhenField { + option (entity).kind = PROJECTION; + + compiler.FieldRef id = 1; + + compiler.Field subject = 2; + + string error_message = 3; + + Time bound = 4; + + spine.tools.time.validation.TimeFieldType type = 5; +} +``` + +The `id` field type must match the event `id` type exactly. Without this match, the framework +cannot route `WhenFieldDiscovered` events to the correct `WhenField` view instance. + +## What's next + +- [Register the option](register-the-option.md) +- [Back to Custom Validation](../) diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md new file mode 100644 index 0000000000..d96c55278a --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md @@ -0,0 +1,66 @@ +--- +title: Implement the Generator +description: How to implement an OptionGenerator that produces Java validation code. +headline: Documentation +--- + +# Implement the Generator + +The Generator produces Java code for every application of the option within a compiled message +type. The framework calls `codeFor` once per message type and inlines each returned +`SingleOptionCode` into the generated `validate()` method. + +## Class declaration + +```kotlin +internal class WhenGenerator : OptionGeneratorWithConverter() +``` + +Use `OptionGeneratorWithConverter` when your generated code needs to convert a Protobuf field +value to a Java expression — for example, to format a field value as a JSON string in an error +message. This base class injects a `JavaValueConverter` that handles the conversion. Use the +plain `OptionGenerator` base class when no value conversion is needed. + +## Querying the View + +```kotlin +private val allWhenFields by lazy { + querying.select().all() +} +``` + +`querying` is injected by the framework and provides read access to all accumulated View +instances. The `by lazy` delegate is required because `querying` is not available until the +framework initialises the generator; accessing it during construction causes an error. The +query result is cached after the first call. + +## `codeFor` override + +```kotlin +override fun codeFor(type: TypeName): List = + allWhenFields + .filter { it.id.type == type } + .map { GenerateWhen(it, converter).code() } +``` + +The framework calls `codeFor` once for each message type it processes. Filter the view list +by `id.type == type` to select only the fields that belong to the current message. Each +filtered view is passed to a helper that composes the actual `CodeBlock`. + +Each `SingleOptionCode` wraps a `CodeBlock` that is inlined directly into the generated +`validate()` method, so the code must be a valid Java statement or block. + +## Inner-class pattern + +`GenerateWhen` is a private nested class that separates two concerns: + +- The generator (`WhenGenerator`) decides *which* view entries to process. +- The nested class (`GenerateWhen`) decides *how* to turn one view entry into a code string. + +This pattern keeps `codeFor` readable when the code generation logic is non-trivial. For simple +options, the nested class can be replaced by a local function or lambda. + +## What's next + +- [Back to Custom Validation](../) +- [Architecture](../09-developers-guide/architecture.md) diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-reaction.md b/docs/content/docs/validation/05-custom-validation/implement-the-reaction.md new file mode 100644 index 0000000000..669d8cb63c --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/implement-the-reaction.md @@ -0,0 +1,122 @@ +--- +title: Implement the Reaction +description: How to implement a Reaction that discovers and validates a custom option. +headline: Documentation +--- + +# Implement the Reaction + +The Reaction is the entry point for option handling. It subscribes to +`FieldOptionDiscovered` events (or one of the other `*OptionDiscovered` variants), +filters them by option name, validates the option application, and emits a domain event +when the option is applied correctly. + +## Class declaration + +```kotlin +internal class WhenReaction : Reaction() +``` + +The type parameter is the event the Reaction listens to. For a field-level option, use +`FieldOptionDiscovered`. Other variants include `FileOptionDiscovered`, +`MessageOptionDiscovered`, and `OneofOptionDiscovered`. + +## Reaction method + +```kotlin +@React +override fun whenever( + @External @Where(field = OPTION_NAME, equals = WhenOption.NAME) + event: FieldOptionDiscovered +): EitherOf2 { + val field = event.subject + val file = event.file + + val timeType = checkFieldType(field, typeSystem, file) + + val option = event.option.value.unpack() + val timeBound = option.`in` + if (timeBound == Time.TIME_UNDEFINED) { + return ignore() + } + + val message = option.errorMsg.ifEmpty { option.descriptorForType.defaultMessage } + message.checkPlaceholders(SUPPORTED_PLACEHOLDERS, field, file, WhenOption.NAME) + + return whenFieldDiscovered { + id = field.ref + subject = field + errorMessage = message + bound = timeBound + type = timeType + }.asA() +} +``` + +### Annotations + +- `@React` marks the method as the reaction handler; only one such method is allowed per class. +- `@External` tells the framework that `FieldOptionDiscovered` originates from the compiler's + bounded context, not the current one. +- `@Where(field = OPTION_NAME, equals = WhenOption.NAME)` narrows the subscription so that + `whenever` receives only events where the option name equals `"when"`. `OPTION_NAME` is a + constant from `io.spine.tools.validation` that names the filter field. Without this filter, + the Reaction would be called for every field option discovered during compilation. + +### Return type + +`EitherOf2` expresses that the method either emits a domain +event or signals that no reaction should take place. + +## Three possible outcomes + +### 1. Unsupported field type + +The main `whenever` snippet calls `checkFieldType(field, typeSystem, file)`, which is a +private helper that wraps `Compilation.check`: + +```kotlin +private fun checkFieldType(field: Field, typeSystem: TypeSystem, file: File): TimeFieldType { + val timeType = typeSystem.determineTimeType(field.type) + Compilation.check(timeType != TFT_UNKNOWN, file, field.span) { + "The field type `${field.type.name}` of `${field.qualifiedName}` " + + "is not supported by the `(${WhenOption.NAME})` option." + } + return timeType +} +``` + +`Compilation.check` throws a compilation exception when the condition is `false`, causing the +build to fail with the supplied message pointing to the source file and span. Extracting the +check into a helper keeps the main reaction method readable and allows the helper to also return +the resolved `TimeFieldType` for later use. + +### 2. Disabled option + +Short-circuit with `return ignore()` (which returns `NoReaction`) when the option value equals +the sentinel `TIME_UNDEFINED`. This represents a correctly formed but effectively disabled +option — for example, `[(when).in = TIME_UNDEFINED]`. No domain event is emitted and no code is +generated for that field. + +### 3. Valid, enabled option + +Validate the error message template with `checkPlaceholders`, then build and emit the domain +event using the Kotlin DSL: + +```kotlin +return whenFieldDiscovered { + id = field.ref + subject = field + errorMessage = message + bound = timeBound + type = timeType +}.asA() +``` + +`checkPlaceholders` reports a compilation error if the template contains placeholder names that +the option does not support. `.asA()` wraps the event in the `EitherOf2` left slot. + +## What's next + +- [Implement the View](implement-the-view.md) +- [Back to Custom Validation](../) diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-view.md b/docs/content/docs/validation/05-custom-validation/implement-the-view.md new file mode 100644 index 0000000000..0b59f338ba --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/implement-the-view.md @@ -0,0 +1,49 @@ +--- +title: Implement the View +description: How to implement a View that accumulates option data for code generation. +headline: Documentation +--- + +# Implement the View + +The View is a Spine projection that persists the data emitted by the Reaction. The Generator +queries the View to obtain the information it needs to produce Java code. + +## Class declaration + +```kotlin +internal class WhenFieldView : View() +``` + +The three type parameters are: + +| Parameter | Type | Description | +| --- | --- | --- | +| ID | `FieldRef` | Entity identity; must match the `id` field type in the Protobuf view state. | +| State | `WhenField` | The generated Protobuf message that holds the accumulated data. | +| Builder | `WhenField.Builder` | The corresponding Protobuf builder, used by the `alter { }` DSL. | + +## Subscriber method + +```kotlin +@Subscribe +fun on(e: WhenFieldDiscovered) = alter { + subject = e.subject + errorMessage = e.errorMessage + bound = e.bound + type = e.type +} +``` + +The `@Subscribe` annotation registers `on` as the event handler. The `alter { }` DSL block +provides access to the state builder; any assignments inside the block are applied atomically +when the block exits. One `@Subscribe` method is required for each event type the View handles. + +Views only accumulate data. Validation and business logic belong in the Reaction; by the time +an event reaches the View, the Reaction has already confirmed that the option application is +correct. + +## What's next + +- [Implement the Generator](implement-the-generator.md) +- [Back to Custom Validation](../) diff --git a/docs/content/docs/validation/05-custom-validation/register-the-option.md b/docs/content/docs/validation/05-custom-validation/register-the-option.md new file mode 100644 index 0000000000..2b9d9de98c --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/register-the-option.md @@ -0,0 +1,81 @@ +--- +title: Register the option +description: How to register the Protobuf extension and wire up the ValidationOption. +headline: Documentation +--- + +# Register the option + +Two registrations are required before the build plugin can use a custom option: + +1. Register the Protobuf extension so the descriptor machinery can resolve it at runtime. +2. Wire up the Reaction, View, and Generator via `ValidationOption`. + +## Register the proto extension + +Create a class that implements `OptionsProvider` and annotate it with +`@AutoService(OptionsProvider.class)`: + +```java +import com.google.auto.service.AutoService; +import com.google.protobuf.ExtensionRegistry; +import io.spine.option.OptionsProvider; + +@AutoService(OptionsProvider.class) +public class TimeOptionsProvider implements OptionsProvider { + + @Override + public void registerIn(ExtensionRegistry registry) { + TimeOptionsProto.registerAllExtensions(registry); + } +} +``` + +Call `registerAllExtensions` on the generated outer class of the `.proto` file that contains +the `extend` block. The `java_outer_classname` option in the proto file controls this class name +(for example, `option java_outer_classname = "TimeOptionsProto"`). Without this registration, +the Protobuf runtime cannot deserialize the extension field and the option will be silently +ignored. + +## Wire up ValidationOption + +Create a class that implements `ValidationOption` and annotate it with +`@AutoService(ValidationOption::class)`: + +```kotlin +import com.google.auto.service.AutoService +import io.spine.tools.validation.java.ValidationOption +import io.spine.tools.validation.java.generate.OptionGenerator + +@AutoService(ValidationOption::class) +public class WhenOption : ValidationOption { + + public companion object { + public const val NAME: String = "when" + } + + override val reactions: Set> = setOf(WhenReaction()) + + override val view: Set>> = setOf(WhenFieldView::class.java) + + override val generator: OptionGenerator = WhenGenerator() +} +``` + +Key points: + +- `NAME` is a `const val` in the companion object. Its value must exactly match the field name + used in the `extend` block (for example, `when`). The Reaction uses this constant in its + `@Where` filter to subscribe only to events for this option. +- `reactions` can contain multiple `Reaction` instances; `view` can list multiple `View` classes. +- `generator` accepts exactly **one** `OptionGenerator`. Only one generator per + `ValidationOption` is allowed. + +Both `OptionsProvider` and `ValidationOption` are discovered via Java `ServiceLoader`, so the +`@AutoService` annotation must be present and the annotation processor must run during +compilation. + +## What's next + +- [Implement the Reaction](implement-the-reaction.md) +- [Back to Custom Validation](../) From 6ae7a1ebf37c0a2e7075887e7fd764fd4106e03c Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 17:43:20 +0100 Subject: [PATCH 11/35] Reoder sections in navigation --- .../docs/validation/2-0-0-snapshot/sidenav.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index 3a801811e3..1e015bd3c5 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -58,7 +58,20 @@ - page: Using `ValidatorRegistry` file_path: 04-validators/validator-registry - page: Custom validation - file_path: 05-custom-validation + key: 05-custom-validation + children: + - page: Custom validation + file_path: 05-custom-validation + - page: Declare the option in Protobuf + file_path: 05-custom-validation/declare-the-option + - page: Register the option + file_path: 05-custom-validation/register-the-option + - page: Implement the Reaction + file_path: 05-custom-validation/implement-the-reaction + - page: Implement the View + file_path: 05-custom-validation/implement-the-view + - page: Implement the Generator + file_path: 05-custom-validation/implement-the-generator - page: Developer’s guide key: 09-developers-guide children: From da66821aca7bad37943a1ced87f6ea0aacc14fae Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 18:00:05 +0100 Subject: [PATCH 12/35] Create "Running example" section --- .../docs/validation/05-custom-validation/_index.md | 14 +++++++++++--- .../05-custom-validation/declare-the-option.md | 2 -- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/content/docs/validation/05-custom-validation/_index.md b/docs/content/docs/validation/05-custom-validation/_index.md index 7c4086e3c5..55d6442de3 100644 --- a/docs/content/docs/validation/05-custom-validation/_index.md +++ b/docs/content/docs/validation/05-custom-validation/_index.md @@ -6,7 +6,7 @@ headline: Documentation # Custom validation -Users can extend the library by providing custom Protobuf options and code generation logic. +Users can extend the Validation library by providing custom Protobuf options and code generation logic. Follow these steps to create a custom option: @@ -42,6 +42,16 @@ See [Implement the View](implement-the-view.md) for details. Produces Java code for every application of the option within a message type. See [Implement the Generator](implement-the-generator.md) for details. +## Running example + +Throughout this section, the `(when)` option from the +[Spine Time](https://github.com/SpineEventEngine/time) library serves as the running example. +Spine Time is a library of Protobuf-based date and time types for business models. It defines +its own Protobuf message types — such as `LocalDate`, `LocalTime`, and `ZonedDateTime` — and +provides converters to and from the standard Java Time API. Among other features, it ships a +`(when)` validation option that constrains a time-typed field to hold either a past or a +future value. + ## What’s next - [Declare the option in Protobuf](declare-the-option.md) @@ -51,5 +61,3 @@ See [Implement the Generator](implement-the-generator.md) for details. - [Implement the Generator](implement-the-generator.md) - [Using validators](../04-validators/) - Learn where this plugs in: [Architecture](../09-developers-guide/architecture.md). - -The `:tests:extensions` module contains a full example of the custom `(currency)` option. diff --git a/docs/content/docs/validation/05-custom-validation/declare-the-option.md b/docs/content/docs/validation/05-custom-validation/declare-the-option.md index d6520461b1..7288e1ec52 100644 --- a/docs/content/docs/validation/05-custom-validation/declare-the-option.md +++ b/docs/content/docs/validation/05-custom-validation/declare-the-option.md @@ -12,8 +12,6 @@ A custom validation option requires three distinct Protobuf definitions: 2. A domain event emitted by the Reaction when a valid option application is found. 3. A view state that persists the event data for the Generator to query. -The examples below are taken from the `(when)` option in `docs/_time/`. - ## Declare the option message An option is declared as a Protobuf `extend` block targeting one of the standard From fed03a9cc28d0ac32657d94aa809aab11fc96bf4 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 18:24:09 +0100 Subject: [PATCH 13/35] Separate the description of adding events and views --- .../validation/05-custom-validation/_index.md | 34 +++++----- .../declare-event-and-view.md | 64 +++++++++++++++++++ .../declare-the-option.md | 59 ++--------------- .../implement-the-generator.md | 10 +-- .../implement-the-reaction.md | 16 ++--- .../implement-the-view.md | 18 +++--- .../register-the-option.md | 6 +- .../validation/2-0-0-snapshot/sidenav.yml | 8 ++- 8 files changed, 116 insertions(+), 99 deletions(-) create mode 100644 docs/content/docs/validation/05-custom-validation/declare-event-and-view.md diff --git a/docs/content/docs/validation/05-custom-validation/_index.md b/docs/content/docs/validation/05-custom-validation/_index.md index 55d6442de3..c3e6858d0c 100644 --- a/docs/content/docs/validation/05-custom-validation/_index.md +++ b/docs/content/docs/validation/05-custom-validation/_index.md @@ -10,13 +10,14 @@ Users can extend the Validation library by providing custom Protobuf options and Follow these steps to create a custom option: -1. [Declare](declare-the-option.md) a Protobuf extension, a domain event, and a view state in - your `.proto` files. -2. [Register](register-the-option.md) the Protobuf extension via `io.spine.option.OptionsProvider` +1. [Declare the option](declare-the-option.md) as a Protobuf extension in your `.proto` files. +2. [Register the option](register-the-option.md) via `io.spine.option.OptionsProvider` and wire up the entities via `io.spine.tools.validation.java.ValidationOption`. -3. [Implement the Reaction](implement-the-reaction.md) — discovers and validates the option. -4. [Implement the View](implement-the-view.md) — accumulates valid option applications. -5. [Implement the Generator](implement-the-generator.md) — generates Java code for the option. +3. [Declare the event and view state](declare-event-and-view.md) — the Protobuf types that + track the option's discovery during compilation. +4. [Implement the `Reaction`](implement-the-reaction.md) — discovers and validates the option. +5. [Implement the `View`](implement-the-view.md) — accumulates valid option applications. +6. [Implement the `Generator`](implement-the-generator.md) — generates Java code for the option. Below is a workflow diagram for a typical option: @@ -27,20 +28,20 @@ This allows building more complex models using more entities and events. ## Components -### Reaction +### `Reaction` Subscribes to `*OptionDiscovered` events and filters them by option name. -See [Implement the Reaction](implement-the-reaction.md) for details. +See [Implement the `Reaction`](implement-the-reaction.md) for details. -### View +### `View` -Accumulates events emitted by the Reaction so the Generator can query them. -See [Implement the View](implement-the-view.md) for details. +Accumulates events emitted by the `Reaction` so the `Generator` can query them. +See [Implement the `View`](implement-the-view.md) for details. -### Generator +### `Generator` Produces Java code for every application of the option within a message type. -See [Implement the Generator](implement-the-generator.md) for details. +See [Implement the `Generator`](implement-the-generator.md) for details. ## Running example @@ -56,8 +57,9 @@ future value. - [Declare the option in Protobuf](declare-the-option.md) - [Register the option](register-the-option.md) -- [Implement the Reaction](implement-the-reaction.md) -- [Implement the View](implement-the-view.md) -- [Implement the Generator](implement-the-generator.md) +- [Declare the event and view state](declare-event-and-view.md) +- [Implement the `Reaction`](implement-the-reaction.md) +- [Implement the `View`](implement-the-view.md) +- [Implement the `Generator`](implement-the-generator.md) - [Using validators](../04-validators/) - Learn where this plugs in: [Architecture](../09-developers-guide/architecture.md). diff --git a/docs/content/docs/validation/05-custom-validation/declare-event-and-view.md b/docs/content/docs/validation/05-custom-validation/declare-event-and-view.md new file mode 100644 index 0000000000..4e18aac2d7 --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/declare-event-and-view.md @@ -0,0 +1,64 @@ +--- +title: Declare the event and view state +description: How to define the domain event and view state Protobuf types for a custom option. +headline: Documentation +--- + +# Declare the event and view state + +After registering the option, declare the Protobuf types that track its discovery during +compilation: a domain event emitted by the `Reaction` when a valid option application is found, +and a view state that persists the event data for the `Generator` to query. + +## Declare the event + +The `Reaction` emits a domain event carrying all data that the `View` and `Generator` need. The event +travels through the compilation bounded context, so it must be a proper Protobuf message. + +```protobuf +message WhenFieldDiscovered { + + compiler.FieldRef id = 1; + + compiler.Field subject = 2; + + string error_message = 3; + + Time bound = 4; + + spine.tools.time.validation.TimeFieldType type = 5; +} +``` + +The `id` field must be the **first** field and must be the same type that the `View` uses as its +entity identity (`compiler.FieldRef` in this case). The framework uses the identity field to +route the event to the correct `View` instance. + +## Declare the view state + +The view state is the persistent accumulator queried by the `Generator`. It mirrors the event +fields and is marked as a Spine projection: + +```protobuf +message WhenField { + option (entity).kind = PROJECTION; + + compiler.FieldRef id = 1; + + compiler.Field subject = 2; + + string error_message = 3; + + Time bound = 4; + + spine.tools.time.validation.TimeFieldType type = 5; +} +``` + +The `id` field type must match the event `id` type exactly. Without this match, the framework +cannot route `WhenFieldDiscovered` events to the correct `WhenField` view instance. + +## What's next + +- [Implement the `Reaction`](implement-the-reaction.md) +- [Back to Custom Validation](../) diff --git a/docs/content/docs/validation/05-custom-validation/declare-the-option.md b/docs/content/docs/validation/05-custom-validation/declare-the-option.md index 7288e1ec52..cd63046f86 100644 --- a/docs/content/docs/validation/05-custom-validation/declare-the-option.md +++ b/docs/content/docs/validation/05-custom-validation/declare-the-option.md @@ -1,21 +1,18 @@ --- title: Declare the option in Protobuf -description: How to define the option message, domain event, and view state in Protobuf. +description: How to define the option message in Protobuf. headline: Documentation --- # Declare the option in Protobuf -A custom validation option requires three distinct Protobuf definitions: - -1. The option message itself (extends a standard descriptor option type). -2. A domain event emitted by the Reaction when a valid option application is found. -3. A view state that persists the event data for the Generator to query. +An option is declared as a Protobuf `extend` block targeting one of the standard +descriptor option types. ## Declare the option message An option is declared as a Protobuf `extend` block targeting one of the standard -descriptor option types: `google.protobuf.FieldOptions`, `MessageOptions`, and so on. +descriptor option types — `google.protobuf.FieldOptions`, `MessageOptions`, and so on. ```protobuf extend google.protobuf.FieldOptions { @@ -54,54 +51,6 @@ can write `[(when).in = FUTURE]` instead of `[(spine.time.when).in = FUTURE]`. T deliberate trade-off: shorter option syntax at the cost of no package-level namespacing. The `time_options.proto` file explains this choice in its header comment. -## Declare the event - -The Reaction emits a domain event carrying all data that the View and Generator need. The event -travels through the compilation bounded context, so it must be a proper Protobuf message. - -```protobuf -message WhenFieldDiscovered { - - compiler.FieldRef id = 1; - - compiler.Field subject = 2; - - string error_message = 3; - - Time bound = 4; - - spine.tools.time.validation.TimeFieldType type = 5; -} -``` - -The `id` field must be the **first** field and must be the same type that the View uses as its -entity identity (`compiler.FieldRef` in this case). The framework uses the identity field to -route the event to the correct View instance. - -## Declare the view state - -The view state is the persistent accumulator queried by the Generator. It mirrors the event -fields and is marked as a Spine projection: - -```protobuf -message WhenField { - option (entity).kind = PROJECTION; - - compiler.FieldRef id = 1; - - compiler.Field subject = 2; - - string error_message = 3; - - Time bound = 4; - - spine.tools.time.validation.TimeFieldType type = 5; -} -``` - -The `id` field type must match the event `id` type exactly. Without this match, the framework -cannot route `WhenFieldDiscovered` events to the correct `WhenField` view instance. - ## What's next - [Register the option](register-the-option.md) diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md index d96c55278a..af8626dfa3 100644 --- a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md +++ b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md @@ -1,12 +1,12 @@ --- -title: Implement the Generator +title: 'Implement the `Generator`' description: How to implement an OptionGenerator that produces Java validation code. headline: Documentation --- -# Implement the Generator +# Implement the `Generator` -The Generator produces Java code for every application of the option within a compiled message +The `Generator` produces Java code for every application of the option within a compiled message type. The framework calls `codeFor` once per message type and inlines each returned `SingleOptionCode` into the generated `validate()` method. @@ -21,7 +21,7 @@ value to a Java expression — for example, to format a field value as a JSON st message. This base class injects a `JavaValueConverter` that handles the conversion. Use the plain `OptionGenerator` base class when no value conversion is needed. -## Querying the View +## Querying the `View` ```kotlin private val allWhenFields by lazy { @@ -29,7 +29,7 @@ private val allWhenFields by lazy { } ``` -`querying` is injected by the framework and provides read access to all accumulated View +`querying` is injected by the framework and provides read access to all accumulated `View` instances. The `by lazy` delegate is required because `querying` is not available until the framework initialises the generator; accessing it during construction causes an error. The query result is cached after the first call. diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-reaction.md b/docs/content/docs/validation/05-custom-validation/implement-the-reaction.md index 669d8cb63c..fd61606a8e 100644 --- a/docs/content/docs/validation/05-custom-validation/implement-the-reaction.md +++ b/docs/content/docs/validation/05-custom-validation/implement-the-reaction.md @@ -1,12 +1,12 @@ --- -title: Implement the Reaction -description: How to implement a Reaction that discovers and validates a custom option. +title: 'Implement the `Reaction`' +description: 'How to implement a `Reaction` that discovers and validates a custom option.' headline: Documentation --- -# Implement the Reaction +# Implement the `Reaction` -The Reaction is the entry point for option handling. It subscribes to +The `Reaction` is the entry point for option handling. It subscribes to `FieldOptionDiscovered` events (or one of the other `*OptionDiscovered` variants), filters them by option name, validates the option application, and emits a domain event when the option is applied correctly. @@ -17,11 +17,11 @@ when the option is applied correctly. internal class WhenReaction : Reaction() ``` -The type parameter is the event the Reaction listens to. For a field-level option, use +The type parameter is the event the `Reaction` listens to. For a field-level option, use `FieldOptionDiscovered`. Other variants include `FileOptionDiscovered`, `MessageOptionDiscovered`, and `OneofOptionDiscovered`. -## Reaction method +## `Reaction` method ```kotlin @React @@ -61,7 +61,7 @@ override fun whenever( - `@Where(field = OPTION_NAME, equals = WhenOption.NAME)` narrows the subscription so that `whenever` receives only events where the option name equals `"when"`. `OPTION_NAME` is a constant from `io.spine.tools.validation` that names the filter field. Without this filter, - the Reaction would be called for every field option discovered during compilation. + the `Reaction` would be called for every field option discovered during compilation. ### Return type @@ -118,5 +118,5 @@ the option does not support. `.asA()` wraps the event in the `EitherOf2` left sl ## What's next -- [Implement the View](implement-the-view.md) +- [Implement the `View`](implement-the-view.md) - [Back to Custom Validation](../) diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-view.md b/docs/content/docs/validation/05-custom-validation/implement-the-view.md index 0b59f338ba..92d5e2df98 100644 --- a/docs/content/docs/validation/05-custom-validation/implement-the-view.md +++ b/docs/content/docs/validation/05-custom-validation/implement-the-view.md @@ -1,13 +1,13 @@ --- -title: Implement the View -description: How to implement a View that accumulates option data for code generation. +title: 'Implement the `View`' +description: 'How to implement a `View` that accumulates option data for code generation.' headline: Documentation --- -# Implement the View +# Implement the `View` -The View is a Spine projection that persists the data emitted by the Reaction. The Generator -queries the View to obtain the information it needs to produce Java code. +The `View` is a Spine projection that persists the data emitted by the `Reaction`. The `Generator` +queries the `View` to obtain the information it needs to produce Java code. ## Class declaration @@ -37,13 +37,13 @@ fun on(e: WhenFieldDiscovered) = alter { The `@Subscribe` annotation registers `on` as the event handler. The `alter { }` DSL block provides access to the state builder; any assignments inside the block are applied atomically -when the block exits. One `@Subscribe` method is required for each event type the View handles. +when the block exits. One `@Subscribe` method is required for each event type the `View` handles. -Views only accumulate data. Validation and business logic belong in the Reaction; by the time -an event reaches the View, the Reaction has already confirmed that the option application is +`View`s only accumulate data. Validation and business logic belong in the `Reaction`; by the time +an event reaches the `View`, the `Reaction` has already confirmed that the option application is correct. ## What's next -- [Implement the Generator](implement-the-generator.md) +- [Implement the `Generator`](implement-the-generator.md) - [Back to Custom Validation](../) diff --git a/docs/content/docs/validation/05-custom-validation/register-the-option.md b/docs/content/docs/validation/05-custom-validation/register-the-option.md index 2b9d9de98c..860a30c5e6 100644 --- a/docs/content/docs/validation/05-custom-validation/register-the-option.md +++ b/docs/content/docs/validation/05-custom-validation/register-the-option.md @@ -9,7 +9,7 @@ headline: Documentation Two registrations are required before the build plugin can use a custom option: 1. Register the Protobuf extension so the descriptor machinery can resolve it at runtime. -2. Wire up the Reaction, View, and Generator via `ValidationOption`. +2. Wire up the `Reaction`, `View`, and `Generator` via `ValidationOption`. ## Register the proto extension @@ -65,7 +65,7 @@ public class WhenOption : ValidationOption { Key points: - `NAME` is a `const val` in the companion object. Its value must exactly match the field name - used in the `extend` block (for example, `when`). The Reaction uses this constant in its + used in the `extend` block (for example, `when`). The `Reaction` uses this constant in its `@Where` filter to subscribe only to events for this option. - `reactions` can contain multiple `Reaction` instances; `view` can list multiple `View` classes. - `generator` accepts exactly **one** `OptionGenerator`. Only one generator per @@ -77,5 +77,5 @@ compilation. ## What's next -- [Implement the Reaction](implement-the-reaction.md) +- [Declare the event and view state](declare-event-and-view.md) - [Back to Custom Validation](../) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index 1e015bd3c5..afe8b0354d 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -66,11 +66,13 @@ file_path: 05-custom-validation/declare-the-option - page: Register the option file_path: 05-custom-validation/register-the-option - - page: Implement the Reaction + - page: Declare the event and view state + file_path: 05-custom-validation/declare-event-and-view + - page: 'Implement the `Reaction`' file_path: 05-custom-validation/implement-the-reaction - - page: Implement the View + - page: 'Implement the `View`' file_path: 05-custom-validation/implement-the-view - - page: Implement the Generator + - page: 'Implement the `Generator`' file_path: 05-custom-validation/implement-the-generator - page: Developer’s guide key: 09-developers-guide From 3a244c5934f2f06649a41a8185b828b49442e93f Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 18:28:47 +0100 Subject: [PATCH 14/35] Remove redundant links --- docs/content/docs/validation/05-custom-validation/_index.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/content/docs/validation/05-custom-validation/_index.md b/docs/content/docs/validation/05-custom-validation/_index.md index c3e6858d0c..3088372c09 100644 --- a/docs/content/docs/validation/05-custom-validation/_index.md +++ b/docs/content/docs/validation/05-custom-validation/_index.md @@ -60,6 +60,4 @@ future value. - [Declare the event and view state](declare-event-and-view.md) - [Implement the `Reaction`](implement-the-reaction.md) - [Implement the `View`](implement-the-view.md) -- [Implement the `Generator`](implement-the-generator.md) -- [Using validators](../04-validators/) -- Learn where this plugs in: [Architecture](../09-developers-guide/architecture.md). +- [Implement the `Generator`](implement-the-generator.md). From 9a3e44b903a44002442081c276cdd99e3f222283 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 18:42:14 +0100 Subject: [PATCH 15/35] Use typographic quotes instead of straight ones --- .agents/tasks/document-passing-custom-validation.md | 5 +++++ docs/content/docs/validation/02-concepts/_index.md | 6 +++--- .../content/docs/validation/02-concepts/options-overview.md | 2 +- docs/content/docs/validation/05-custom-validation/_index.md | 6 +++--- 4 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 .agents/tasks/document-passing-custom-validation.md diff --git a/.agents/tasks/document-passing-custom-validation.md b/.agents/tasks/document-passing-custom-validation.md new file mode 100644 index 0000000000..1f6415f018 --- /dev/null +++ b/.agents/tasks/document-passing-custom-validation.md @@ -0,0 +1,5 @@ +# Task: Document Passing Custom Validation + +We need to describe how `ValidationOption` descendant comes into play for a custom +validation option. +This should be a new section which comes after the "Implement the `Generator`" section. diff --git a/docs/content/docs/validation/02-concepts/_index.md b/docs/content/docs/validation/02-concepts/_index.md index de85bc608b..30b48e0da1 100644 --- a/docs/content/docs/validation/02-concepts/_index.md +++ b/docs/content/docs/validation/02-concepts/_index.md @@ -55,7 +55,7 @@ The generated API exposes two main ways to validate data: Generated messages implement `ValidatableMessage` and provide `validate()`, which returns `Optional`. -See [Using the generated code](../01-getting-started/generated-code.md) for end-to-end examples. +See “[Using the generated code](../01-getting-started/generated-code.md)” for end-to-end examples. ## What a violation contains @@ -68,7 +68,7 @@ Each `ConstraintViolation` points to the invalid value and explains what went wr - `field_value` — the invalid value packed as `google.protobuf.Any`. When you need a human-readable message, format the `TemplateString` from the violation. -See [Working with error messages](error-messages.md) for message formatting, +See “[Working with error messages](error-messages.md)” for message formatting, placeholders, and customization. @@ -91,7 +91,7 @@ which message was validated at the top level. If built-in options are not enough, you can add organization-specific options and generate code for them. -See [Custom validation](../05-custom-validation/) for the workflow and a reference example. +See “[Custom validation](../05-custom-validation/)” for the workflow and a reference example. ## What’s next diff --git a/docs/content/docs/validation/02-concepts/options-overview.md b/docs/content/docs/validation/02-concepts/options-overview.md index 11f54d07c2..e9e3eb661c 100644 --- a/docs/content/docs/validation/02-concepts/options-overview.md +++ b/docs/content/docs/validation/02-concepts/options-overview.md @@ -60,7 +60,7 @@ Generated messages and builders provide a small validation-focused API surface: - `buildPartial()` builds without validation. - `validate()` checks an existing instance and returns `Optional`. -See [Using the generated code](../01-getting-started/generated-code.md) for Java and Kotlin examples. +See “[Using the generated code](../01-getting-started/generated-code.md)” for Java and Kotlin examples. ## What does not happen diff --git a/docs/content/docs/validation/05-custom-validation/_index.md b/docs/content/docs/validation/05-custom-validation/_index.md index 3088372c09..621278791c 100644 --- a/docs/content/docs/validation/05-custom-validation/_index.md +++ b/docs/content/docs/validation/05-custom-validation/_index.md @@ -31,17 +31,17 @@ This allows building more complex models using more entities and events. ### `Reaction` Subscribes to `*OptionDiscovered` events and filters them by option name. -See [Implement the `Reaction`](implement-the-reaction.md) for details. +See “[Implement the `Reaction`](implement-the-reaction.md)” for details. ### `View` Accumulates events emitted by the `Reaction` so the `Generator` can query them. -See [Implement the `View`](implement-the-view.md) for details. +See “[Implement the `View`](implement-the-view.md)” for details. ### `Generator` Produces Java code for every application of the option within a message type. -See [Implement the `Generator`](implement-the-generator.md) for details. +See “[Implement the `Generator`](implement-the-generator.md)” for details. ## Running example From ed0380c6cd14d617802b67c7a9bf57f7be5b1f43 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 18:58:21 +0100 Subject: [PATCH 16/35] Teach `/writer` to keep navigation in sync. --- .agents/skills/writer/SKILL.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 5c720265b3..2883a9522e 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -3,7 +3,7 @@ name: writer description: > Write, edit, and restructure user-facing and developer-facing documentation. Use when asked to create/update docs such as `README.md`, `docs/**`, and - other Markdown documentation; + other Markdown documentation, including keeping docs navigation data in sync; when drafting tutorials, guides, troubleshooting pages, or migration notes; and when improving inline API documentation (KDoc) and examples. --- @@ -24,6 +24,27 @@ description: > - `docs/`: longer-form docs (follow existing conventions in that tree). - Source KDoc: API usage, examples, and semantics that belong with the code. +## Keep docs navigation in sync + +- When adding, removing, moving, or renaming a page under + `docs/content/docs/
/`, keep the current version's matching + `sidenav.yml` in sync. +- Use `docs/data/versions.yml` to identify the current documentation version for + that section. The current version is the entry with `is_main: true`; its + `version_id` maps to `docs/data/docs/
//sidenav.yml`. +- Do not update historical version entries or their navigation files unless the + user explicitly asks to edit that historical version. +- Map page files to `file_path` values relative to the current version's + `content_path`, without `.md`; `_index.md` maps to its directory path, such as + `01-getting-started/_index.md` -> `01-getting-started`. +- Keep each `page` label aligned with the page frontmatter `title` unless the + existing navigation intentionally uses a shorter reader-facing label. +- Preserve the existing ordering, nesting, keys, comments, and YAML quoting + style. Remove nav entries for deleted pages and update `file_path` values for + moved pages. +- If a docs content change should not appear in navigation, say so explicitly in + the final response. + ## Follow local documentation conventions - Follow `.agents/documentation-guidelines.md` and `.agents/documentation-tasks.md`. @@ -48,4 +69,3 @@ description: > - For code changes, follow `.agents/running-builds.md`. - For documentation-only changes in Kotlin/Java sources, prefer `./gradlew dokka`. - From d113d10794307a7c415da295592ca623aca230be Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 19:00:32 +0100 Subject: [PATCH 17/35] Teach `/writer` to keep navigation in sync. --- .agents/skills/writer/agents/openai.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.agents/skills/writer/agents/openai.yaml b/.agents/skills/writer/agents/openai.yaml index 44eaa4e241..dd8946abd4 100644 --- a/.agents/skills/writer/agents/openai.yaml +++ b/.agents/skills/writer/agents/openai.yaml @@ -1,5 +1,4 @@ interface: display_name: "Writer" short_description: "Write and update user/developer docs" - default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, keep changes concise and actionable, and include concrete examples and commands where appropriate." - + default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, keep the current docs sidenav.yml in sync with content changes, do not update historical version docs unless explicitly asked, keep changes concise and actionable, and include concrete examples and commands where appropriate." From b5408437d425ef3fade2db3db556af6ee37f039e Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 19:01:47 +0100 Subject: [PATCH 18/35] Rename the openning section --- docs/content/docs/validation/05-custom-validation/_index.md | 4 ++-- docs/data/docs/validation/2-0-0-snapshot/sidenav.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/docs/validation/05-custom-validation/_index.md b/docs/content/docs/validation/05-custom-validation/_index.md index 621278791c..5056de4d6c 100644 --- a/docs/content/docs/validation/05-custom-validation/_index.md +++ b/docs/content/docs/validation/05-custom-validation/_index.md @@ -1,10 +1,10 @@ --- -title: Custom Validation +title: Overview description: Extending the library with custom options and code generation. headline: Documentation --- -# Custom validation +# Overview Users can extend the Validation library by providing custom Protobuf options and code generation logic. diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index afe8b0354d..9b01eaa201 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -60,7 +60,7 @@ - page: Custom validation key: 05-custom-validation children: - - page: Custom validation + - page: Overview file_path: 05-custom-validation - page: Declare the option in Protobuf file_path: 05-custom-validation/declare-the-option From 63aaf179a7030325f54f3fa47fdbbfaef1dc0ebe Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 19:13:24 +0100 Subject: [PATCH 19/35] Trasnform the section into a note block --- .../validation/05-custom-validation/declare-the-option.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/content/docs/validation/05-custom-validation/declare-the-option.md b/docs/content/docs/validation/05-custom-validation/declare-the-option.md index cd63046f86..c88879e593 100644 --- a/docs/content/docs/validation/05-custom-validation/declare-the-option.md +++ b/docs/content/docs/validation/05-custom-validation/declare-the-option.md @@ -44,12 +44,13 @@ The `(default_message)` option on `TimeOption` sets the error template used when does not supply a custom `error_msg`. Field number `73819` is the globally registered extension number for this option; every extension must have a unique number in the allowed range. +{{% note-block class="note" %}} ### Packaging trade-off If you omit the `package` declaration from the `.proto` file that defines the extension, callers can write `[(when).in = FUTURE]` instead of `[(spine.time.when).in = FUTURE]`. This is a -deliberate trade-off: shorter option syntax at the cost of no package-level namespacing. The -`time_options.proto` file explains this choice in its header comment. +deliberate trade-off: shorter option syntax at the cost of no package-level namespacing. +{{% /note-block %}} ## What's next From 1f38b2b93a2047992805a2336cd8e9d0aa698e2d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 19:41:26 +0100 Subject: [PATCH 20/35] Improve description of the `GenerateWhen` --- .../implement-the-generator.md | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md index af8626dfa3..9a355d5fcf 100644 --- a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md +++ b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md @@ -50,15 +50,27 @@ filtered view is passed to a helper that composes the actual `CodeBlock`. Each `SingleOptionCode` wraps a `CodeBlock` that is inlined directly into the generated `validate()` method, so the code must be a valid Java statement or block. -## Inner-class pattern +For complete context, see +[`WhenGenerator.kt`](https://github.com/SpineEventEngine/time/blob/master/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt) +in the Spine Time repository. -`GenerateWhen` is a private nested class that separates two concerns: +## Generated code paths -- The generator (`WhenGenerator`) decides *which* view entries to process. -- The nested class (`GenerateWhen`) decides *how* to turn one view entry into a code string. +The `GenerateWhen.code()` method chooses the Java code shape for a single application of +the `(when)` option: -This pattern keeps `codeFor` readable when the code generation logic is non-trivial. For simple -options, the nested class can be replaced by a local function or lambda. +- For a single message field, it generates one validation block for the field value. +- For a repeated message field, it generates a `for` loop and validates each element inside + that loop. + +Both branches delegate to the same `validateTime(...)` helper, so the time comparison, +violation construction, and placeholder handling stay in one place. The difference is only +where the checked value comes from: the field getter for a single message, or the loop variable +for each repeated element. + +See the full source around +[`GenerateWhen.code()`](https://github.com/SpineEventEngine/time/blob/master/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt#L105-L117) +for the exact generated Java shape. ## What's next From 207abaef1f842da849545ce6a164982eb4d222bf Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 19:52:07 +0100 Subject: [PATCH 21/35] Add task document for a new section --- .../document-passing-custom-validation.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.agents/tasks/document-passing-custom-validation.md b/.agents/tasks/document-passing-custom-validation.md index 1f6415f018..934f471c58 100644 --- a/.agents/tasks/document-passing-custom-validation.md +++ b/.agents/tasks/document-passing-custom-validation.md @@ -3,3 +3,32 @@ We need to describe how `ValidationOption` descendant comes into play for a custom validation option. This should be a new section which comes after the "Implement the `Generator`" section. + +## Current implementation details + +A `ValidationOption` descendants are discovered using the `ServiceLoader` mechanism +by `JavaValidationPlugin` class. This class is a plugin to the Spine Compiler. +Such plugins are passed to the Compiler using the user's classpath in a Gradle build file. +The Validation Gradle plugin arranges this when it is added to a Gradle project. +In order to be discovered, a `ValidationOption` must be present in the user's classpath too. + +There are two scenarios for achieving this: + +1. Add `io.spine.compiler` to the project and use `spineCompiler` configuration +passing the implementation of `ValidationOption` as a dependency. +This is a simple option which would work when a custom validation option is +not frequently used, e.g. in a single project of a multi-module build. + +2. Create a Gradle plugin that would add the `ValidationOption` implementation +to the Compiler's user classpath of the project. This is the option that the Time library uses and +we need to describe it in the documentation as well. +It is more complex but allows to reuse the custom validation option across multiple projects +of a multi-module build or when a custom option is packaged as a library. + +## Gradle plugin implementation + +It's available in the Spine Time module `gradle-plugin` which is locally available +via the directory `docs/_time/gradle-plugin`. It is available via the GitHub repository +of Spine Time too: https://github.com/SpineEventEngine/time. +Use the GitHub repository for the links to the full source code. +Use locally downloaded code for embedding of smaller code samples. From 63035ff798446beb62f9216ef5c8414e6e0368e2 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 20:09:29 +0100 Subject: [PATCH 22/35] Document passing to the Spine Compiler --- .../implement-the-generator.md | 2 +- .../05-custom-validation/pass-to-compiler.md | 77 +++++++++++++++++++ .../validation/2-0-0-snapshot/sidenav.yml | 4 +- 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 docs/content/docs/validation/05-custom-validation/pass-to-compiler.md diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md index 9a355d5fcf..80d2c46a7d 100644 --- a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md +++ b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md @@ -74,5 +74,5 @@ for the exact generated Java shape. ## What's next +- [Pass the option to the Compiler](pass-to-compiler.md) - [Back to Custom Validation](../) -- [Architecture](../09-developers-guide/architecture.md) diff --git a/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md b/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md new file mode 100644 index 0000000000..e97a615736 --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md @@ -0,0 +1,77 @@ +--- +title: 'Pass the option to the Compiler' +description: How to make a ValidationOption implementation available to the Spine Compiler. +headline: Documentation +--- + +# Pass the option to the Compiler + +`JavaValidationPlugin` — the Spine Compiler plugin that generates Java validation code — +discovers `ValidationOption` implementations via the Java `ServiceLoader` mechanism. To be +found, a `ValidationOption` implementation must be present on the Compiler's user classpath. + +The `io.spine.validation` Gradle plugin places `JavaValidationPlugin` itself on that classpath +automatically. Adding your custom `ValidationOption` requires one additional step. + +There are two approaches: + +1. **Direct dependency** — add the implementation module to the `spineCompiler` dependency + configuration. Use this for a single project or a single module in a multi-module build. + +2. **Gradle plugin** — wrap the classpath configuration in a Gradle plugin. Use this when + the custom option is packaged as a library or shared across multiple modules. + +## Direct dependency + +The `io.spine.validation` plugin registers a `spineCompiler` dependency configuration that +populates the Compiler's user classpath. Add your implementation as a `spineCompiler` dependency: + +```kotlin +dependencies { + spineCompiler("com.example:my-validation-option:1.0.0") +} +``` + +When the Compiler runs, all `spineCompiler` artifacts are placed on the user classpath and +`ServiceLoader` can discover your `ValidationOption` implementation. + +## Gradle plugin + +When the custom option is distributed as a library or reused across projects, a dedicated +Gradle plugin is the preferred approach. The plugin registers the validation module on the +Compiler's user classpath whenever `io.spine.validation` is applied to a project. + +The Spine Time library uses this pattern. Its Gradle plugin reacts to the presence of +`io.spine.validation` and calls `addUserClasspathDependency`: + +```kotlin +import io.spine.tools.compiler.gradle.api.addUserClasspathDependency + +private fun Project.passValidationToCompiler() { + pluginManager.withPlugin(TimeValidation.validationPluginId) { + addUserClasspathDependency(TimeValidation.artifact) + } +} +``` + +`pluginManager.withPlugin(...)` fires only when the `io.spine.validation` plugin is applied to +the target project. Projects that do not use Spine Validation are unaffected. + +`addUserClasspathDependency` from `io.spine.tools.compiler.gradle.api` adds a `MavenArtifact` +to the Compiler's user classpath. The artifact version is resolved from metadata embedded in +the plugin at build time. + +For users of the `io.spine.time` Gradle plugin, no extra configuration is needed: applying the +plugin to a project that has `io.spine.validation` automatically places the `time-validation` +module on the Compiler's classpath. + +See the full source in +[`TimeGradlePlugin.kt`](https://github.com/SpineEventEngine/time/blob/master/gradle-plugin/src/main/kotlin/io/spine/tools/time/gradle/TimeGradlePlugin.kt) +and the artifact definition in +[`TimeValidation.kt`](https://github.com/SpineEventEngine/time/blob/master/gradle-plugin/src/main/kotlin/io/spine/tools/time/gradle/TimeValidation.kt) +in the Spine Time repository. + +## What's next + +- [Back to Custom Validation](../) +- [Architecture](../../09-developers-guide/architecture.md) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index 9b01eaa201..19c3accde2 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -72,8 +72,10 @@ file_path: 05-custom-validation/implement-the-reaction - page: 'Implement the `View`' file_path: 05-custom-validation/implement-the-view - - page: 'Implement the `Generator`' + - page: ‘Implement the `Generator`’ file_path: 05-custom-validation/implement-the-generator + - page: Pass the option to the Compiler + file_path: 05-custom-validation/pass-to-compiler - page: Developer’s guide key: 09-developers-guide children: From 625a41601a90dbab401836976e697d516f40f335 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 20:16:32 +0100 Subject: [PATCH 23/35] Add the Summary page for the "Custom validation" section --- .../05-custom-validation/pass-to-compiler.md | 2 +- .../05-custom-validation/summary.md | 46 +++++++++++++++++++ .../validation/2-0-0-snapshot/sidenav.yml | 2 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 docs/content/docs/validation/05-custom-validation/summary.md diff --git a/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md b/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md index e97a615736..5c26f047a9 100644 --- a/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md +++ b/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md @@ -73,5 +73,5 @@ in the Spine Time repository. ## What's next -- [Back to Custom Validation](../) +- [Summary](summary.md) - [Architecture](../../09-developers-guide/architecture.md) diff --git a/docs/content/docs/validation/05-custom-validation/summary.md b/docs/content/docs/validation/05-custom-validation/summary.md new file mode 100644 index 0000000000..0f0670e133 --- /dev/null +++ b/docs/content/docs/validation/05-custom-validation/summary.md @@ -0,0 +1,46 @@ +--- +title: 'Summary' +description: A recap of the steps for implementing and integrating a custom validation option. +headline: Documentation +--- + +# Summary + +This section walked through the complete workflow for adding a custom validation option to the +Spine Validation library, from declaring the option in Protobuf to making it available to the +Spine Compiler. + +## What you covered + +- **[Declare the option in Protobuf](declare-the-option.md)** — define a Protobuf `extend` + block targeting a standard descriptor option type, with a unique field number and an error + message template that supports named placeholders. + +- **[Register the option](register-the-option.md)** — wire an `OptionsProvider` to register the + Protobuf extension at runtime, and a `ValidationOption` to bind the option name to its + `Reaction`, `View`, and `Generator`. Both are discovered via Java `ServiceLoader`. + +- **[Declare the event and view state](declare-event-and-view.md)** — define a domain event + emitted when the option is encountered, and a projection state message the `Generator` + queries to accumulate option data. + +- **[Implement the `Reaction`](implement-the-reaction.md)** — subscribe to `FieldOptionDiscovered` + events, filter by option name, validate the field type and option value, and emit the domain + event — or signal no reaction when the option is disabled. + +- **[Implement the `View`](implement-the-view.md)** — build a projection that accumulates event + data, making the full set of option applications queryable by the `Generator`. + +- **[Implement the `Generator`](implement-the-generator.md)** — query the `View` and produce + Java validation code inlined into the generated `validate()` method, handling both single + and repeated field cardinalities. + +- **[Pass the option to the Compiler](pass-to-compiler.md)** — place the `ValidationOption` + implementation on the Compiler's user classpath via a direct `spineCompiler` dependency or + a distributable Gradle plugin. + +## What's next + +- Explore library internals: [Architecture](../09-developers-guide/architecture.md) +- See the modules that back this pipeline: + [Key modules](../09-developers-guide/key-modules.md) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index 19c3accde2..a97e5135af 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -76,6 +76,8 @@ file_path: 05-custom-validation/implement-the-generator - page: Pass the option to the Compiler file_path: 05-custom-validation/pass-to-compiler + - page: Summary + file_path: 05-custom-validation/summary - page: Developer’s guide key: 09-developers-guide children: From 07c177372bbbe2bf134d92c9325125bb34370509 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 20:28:34 +0100 Subject: [PATCH 24/35] Fix quoting title in navigation --- docs/data/docs/validation/2-0-0-snapshot/sidenav.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml index a97e5135af..afdc7f1821 100644 --- a/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml +++ b/docs/data/docs/validation/2-0-0-snapshot/sidenav.yml @@ -72,7 +72,7 @@ file_path: 05-custom-validation/implement-the-reaction - page: 'Implement the `View`' file_path: 05-custom-validation/implement-the-view - - page: ‘Implement the `Generator`’ + - page: 'Implement the `Generator`' file_path: 05-custom-validation/implement-the-generator - page: Pass the option to the Compiler file_path: 05-custom-validation/pass-to-compiler From 2d6101b17d6b4fea7ce2b9bf1205de4ec9844ff8 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 20:44:37 +0100 Subject: [PATCH 25/35] Bump CoreJvm Compiler -> `2.0.0-SNAPSHOT.063` --- .../main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 2333e4ce7e..3f4dfab758 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ object CoreJvmCompiler { /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.062" + const val version = "2.0.0-SNAPSHOT.063" /** * The ID of the Gradle plugin. From 7f7039680cd630ceea28a9ed5d782101709c9b45 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 20:44:53 +0100 Subject: [PATCH 26/35] Fix bottom nav link --- .../docs/validation/05-custom-validation/pass-to-compiler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md b/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md index 5c26f047a9..d915f5e63d 100644 --- a/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md +++ b/docs/content/docs/validation/05-custom-validation/pass-to-compiler.md @@ -74,4 +74,4 @@ in the Spine Time repository. ## What's next - [Summary](summary.md) -- [Architecture](../../09-developers-guide/architecture.md) +- [Architecture](../09-developers-guide/architecture.md) From 05018f73e3727ad199bf06f787b0c3c8fa094bdf Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 20:45:12 +0100 Subject: [PATCH 27/35] Describe handling `@AutoService` --- .../register-the-option.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/content/docs/validation/05-custom-validation/register-the-option.md b/docs/content/docs/validation/05-custom-validation/register-the-option.md index 860a30c5e6..af90d917a4 100644 --- a/docs/content/docs/validation/05-custom-validation/register-the-option.md +++ b/docs/content/docs/validation/05-custom-validation/register-the-option.md @@ -75,7 +75,26 @@ Both `OptionsProvider` and `ValidationOption` are discovered via Java `ServiceLo `@AutoService` annotation must be present and the annotation processor must run during compilation. +For Kotlin implementations, configure AutoService's KSP processor in the module that contains the +`@AutoService` classes: + +```kotlin +plugins { + id("com.google.devtools.ksp") +} + +dependencies { + compileOnly("com.google.auto.service:auto-service-annotations:1.1.1") + ksp("dev.zacsweers.autoservice:auto-service-ksp:1.2.0") +} +``` + +If the provider is written in Java, use AutoService's `annotationProcessor` dependency instead of +KSP. See the [AutoService documentation][auto-service] for Gradle examples. + ## What's next - [Declare the event and view state](declare-event-and-view.md) - [Back to Custom Validation](../) + +[auto-service]: https://github.com/google/auto/tree/main/service From f5ef4955ad8bf06ffa180cc8cc96910355350e3b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 20:49:32 +0100 Subject: [PATCH 28/35] Update build time --- dependencies.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dependencies.md b/dependencies.md index 39d714342d..0d1ce1e06d 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1090,7 +1090,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -1791,7 +1791,7 @@ This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -1805,7 +1805,7 @@ This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:54 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -2864,7 +2864,7 @@ This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -3961,7 +3961,7 @@ This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -4015,7 +4015,7 @@ This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -4822,7 +4822,7 @@ This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -5511,7 +5511,7 @@ This report was generated on **Wed Apr 29 20:08:37 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -5976,7 +5976,7 @@ This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -6602,7 +6602,7 @@ This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -7170,7 +7170,7 @@ This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -7781,7 +7781,7 @@ This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -8526,7 +8526,7 @@ This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -8766,7 +8766,7 @@ This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -9116,6 +9116,6 @@ This report was generated on **Wed Apr 29 20:08:35 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Apr 29 20:08:36 WEST 2026** using +This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file From 6c618e8d471405368c2442b2521e66b61fb9a397 Mon Sep 17 00:00:00 2001 From: Alexander Yevsyukov Date: Thu, 30 Apr 2026 23:05:53 +0300 Subject: [PATCH 29/35] Improve the wording for `ValidationOption` implementations Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .agents/tasks/document-passing-custom-validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.agents/tasks/document-passing-custom-validation.md b/.agents/tasks/document-passing-custom-validation.md index 934f471c58..5865232aa4 100644 --- a/.agents/tasks/document-passing-custom-validation.md +++ b/.agents/tasks/document-passing-custom-validation.md @@ -6,8 +6,8 @@ This should be a new section which comes after the "Implement the `Generator`" s ## Current implementation details -A `ValidationOption` descendants are discovered using the `ServiceLoader` mechanism -by `JavaValidationPlugin` class. This class is a plugin to the Spine Compiler. +`ValidationOption` implementations are discovered using the `ServiceLoader` mechanism +by the `JavaValidationPlugin` class. This class is a plugin to the Spine Compiler. Such plugins are passed to the Compiler using the user's classpath in a Gradle build file. The Validation Gradle plugin arranges this when it is added to a Gradle project. In order to be discovered, a `ValidationOption` must be present in the user's classpath too. From c8e9d3f62097bd0d2b94c4ec8643bb21cc05aae7 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 21:07:01 +0100 Subject: [PATCH 30/35] Remove task documents --- .../document-passing-custom-validation.md | 34 --- .../tasks/extend-custom-validation-docs.md | 253 ------------------ 2 files changed, 287 deletions(-) delete mode 100644 .agents/tasks/document-passing-custom-validation.md delete mode 100644 .agents/tasks/extend-custom-validation-docs.md diff --git a/.agents/tasks/document-passing-custom-validation.md b/.agents/tasks/document-passing-custom-validation.md deleted file mode 100644 index 934f471c58..0000000000 --- a/.agents/tasks/document-passing-custom-validation.md +++ /dev/null @@ -1,34 +0,0 @@ -# Task: Document Passing Custom Validation - -We need to describe how `ValidationOption` descendant comes into play for a custom -validation option. -This should be a new section which comes after the "Implement the `Generator`" section. - -## Current implementation details - -A `ValidationOption` descendants are discovered using the `ServiceLoader` mechanism -by `JavaValidationPlugin` class. This class is a plugin to the Spine Compiler. -Such plugins are passed to the Compiler using the user's classpath in a Gradle build file. -The Validation Gradle plugin arranges this when it is added to a Gradle project. -In order to be discovered, a `ValidationOption` must be present in the user's classpath too. - -There are two scenarios for achieving this: - -1. Add `io.spine.compiler` to the project and use `spineCompiler` configuration -passing the implementation of `ValidationOption` as a dependency. -This is a simple option which would work when a custom validation option is -not frequently used, e.g. in a single project of a multi-module build. - -2. Create a Gradle plugin that would add the `ValidationOption` implementation -to the Compiler's user classpath of the project. This is the option that the Time library uses and -we need to describe it in the documentation as well. -It is more complex but allows to reuse the custom validation option across multiple projects -of a multi-module build or when a custom option is packaged as a library. - -## Gradle plugin implementation - -It's available in the Spine Time module `gradle-plugin` which is locally available -via the directory `docs/_time/gradle-plugin`. It is available via the GitHub repository -of Spine Time too: https://github.com/SpineEventEngine/time. -Use the GitHub repository for the links to the full source code. -Use locally downloaded code for embedding of smaller code samples. diff --git a/.agents/tasks/extend-custom-validation-docs.md b/.agents/tasks/extend-custom-validation-docs.md deleted file mode 100644 index b32b74088d..0000000000 --- a/.agents/tasks/extend-custom-validation-docs.md +++ /dev/null @@ -1,253 +0,0 @@ -# Task: Extend "Custom validation" documentation - -## Goal - -Extend `docs/content/docs/validation/05-custom-validation/_index.md` with step-by-step -guidance on implementing a custom validation option and its code generator. The existing -content provides a correct high-level overview; the task is to add concrete detail and -working code examples derived from the `(when)` option implementation in -`docs/_time/validation/`. - -**Target audience**: a developer integrating a new custom option into an existing -Spine-based build plugin. - -**Acceptance criteria**: after reading the extended section, a developer can implement all -required artefacts (proto, Kotlin classes, `@AutoService` registrations) for a new -field-level option without consulting the source code. - ---- - -## Source references - -All code examples should be verified against and derived from these files: - -| Artefact | Path | -|---|---| -| Proto option definition | `docs/_time/time/src/main/proto/spine/time_options.proto` | -| Event proto message | `docs/_time/validation/src/main/proto/spine/tools/time/validation/events.proto` | -| View state proto message | `docs/_time/validation/src/main/proto/spine/tools/time/validation/views.proto` | -| `ValidationOption` + `Reaction` + `View` | `docs/_time/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenOption.kt` | -| `OptionGenerator` | `docs/_time/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt` | -| `OptionsProvider` registration | `tests/extensions/src/main/kotlin/io/spine/tools/validation/test/CustomOptionsProvider.kt` | -| `ValidationOption` SPI entry-point | `tests/extensions/src/main/kotlin/io/spine/tools/validation/test/CurrencyOption.kt` | - ---- - -## Gaps in current documentation - -The current `_index.md` is missing: - -1. **Proto artefacts** — no explanation of the three distinct proto definitions required: - - the option message itself (e.g. `TimeOption` + `Time` enum, extension field), - - the domain event emitted by the reaction (e.g. `WhenFieldDiscovered`), - - the view state accumulator (e.g. `WhenField` with `(entity).kind = PROJECTION`). -2. **`OptionsProvider` registration** — mentioned in step 2 but no code shown. -3. **`ValidationOption` entry-point** — no code showing the `@AutoService` annotation, - the `NAME` constant convention, or how reactions/view/generator are wired together. -4. **Reaction** — does not explain the `@Where` filter, the `EitherOf2` - return type, the "disabled option" short-circuit (e.g. `TIME_UNDEFINED`), or the - `checkPlaceholders` / `Compilation.check` error-reporting API. -5. **View** — does not show the proto backing message, the `FieldRef` identity pattern, or - the `alter { }` DSL. -6. **Generator** — does not explain `OptionGeneratorWithConverter`, lazy view querying, or - `SingleOptionCode`. - ---- - -## File structure - -Mirror the convention used in `docs/content/docs/validation/04-validators/`: -the `_index.md` provides the overview and "What's next" navigation; each detailed -topic lives in its own page. - -``` -docs/content/docs/validation/05-custom-validation/ - _index.md ← existing file, update only - declare-the-option.md ← new - register-the-option.md ← new - implement-the-reaction.md ← new - implement-the-view.md ← new - implement-the-generator.md ← new -``` - ---- - -## Changes to `_index.md` - -- Keep the existing high-level 4-step overview and the workflow diagram unchanged. -- Replace the detailed prose sub-sections ("Reaction", "View", "Generator") with a - short one-liner each that links to the corresponding new page. -- Update the "What's next" block to include links to all five new pages. - ---- - -## Page specifications - -### `declare-the-option.md` - -**Front-matter title**: `Declare the option in Protobuf` - -Three sub-sections, each with a prose introduction and a code snippet. - -#### Declare the option message - -- Explain that the option is a Protobuf `extend` block targeting one of the standard - descriptor option types (`FieldOptions`, `MessageOptions`, etc.). -- Show a trimmed version of `time_options.proto`: - - `extend google.protobuf.FieldOptions { TimeOption when = 73819; }` - - the `TimeOption` message with `Time in = 1` and `string error_msg = 3` - - the `Time` enum (`TIME_UNDEFINED`, `PAST`, `FUTURE`) - - the `(default_message)` annotation on `TimeOption` -- Note: omit `package` from the proto file if shorter option usage in `.proto` files is - desired (cite the comment in `time_options.proto` that explains this trade-off). - -#### Declare the event - -- Explain that the Reaction emits a domain event that carries all data the View and - Generator need. -- Show a trimmed version of `events.proto`: - - `message WhenFieldDiscovered` with `compiler.FieldRef id = 1`, `compiler.Field subject`, - `string error_message`, `Time bound`, `TimeFieldType type`. -- Note: `id` must be the first field and must be the same type that the View uses as its - identity (`FieldRef` in this case). - -#### Declare the view state - -- Explain that the view state is the persistent accumulator queried by the Generator. -- Show a trimmed version of `views.proto`: - - `message WhenField` with `option (entity).kind = PROJECTION` and the same fields as - the event. -- Explain that the `id` field must match the event `id` type so the framework routes the - event to the correct view instance. - ---- - -### `register-the-option.md` - -**Front-matter title**: `Register the option` - -Two sub-sections. - -#### Register the proto extension - -- Show the `@AutoService(OptionsProvider::class)` pattern from `CustomOptionsProvider.kt` - (call `registerAllExtensions` on the generated outer class of the proto file containing - the `extend` block). -- Explain that this makes the option visible to the Protobuf descriptor machinery at - runtime. - -#### Wire up ValidationOption - -- Show the full `WhenOption` class (public API only, omit private helpers): - - `@AutoService(ValidationOption::class)` annotation - - `companion object { const val NAME = "when" }` — explain this string is the value - matched by `@Where` in the Reaction. - - `override val reactions`, `override val view`, `override val generator` -- Note: one `ValidationOption` per custom option; only one generator is allowed per - `ValidationOption`. - ---- - -### `implement-the-reaction.md` - -**Front-matter title**: `Implement the Reaction` - -- Show the class declaration: `internal class WhenReaction : Reaction()`. -- Show the reaction method signature with its annotations: - ```kotlin - @React - override fun whenever( - @External @Where(field = OPTION_NAME, equals = WhenOption.NAME) - event: FieldOptionDiscovered - ): EitherOf2 - ``` -- Explain each annotation: - - `@External` — the event originates from the compiler's bounded context. - - `@Where` — narrows the subscription to events for this option only; `OPTION_NAME` is - a constant from `io.spine.tools.validation`. -- Describe the three possible outcomes: - 1. **Unsupported field type** — call `Compilation.check(...)` to emit a compile error - and stop processing. - 2. **Disabled option** — short-circuit with `return ignore()` (emits `NoReaction`) when - the option value is the sentinel `TIME_UNDEFINED`. - 3. **Valid, enabled option** — validate the error message template via - `checkPlaceholders(SUPPORTED_PLACEHOLDERS, field, file, WhenOption.NAME)`, then build - and return the domain event using the Kotlin DSL - (`whenFieldDiscovered { ... }.asA()`). -- Include a concise end-to-end code snippet of the `WhenReaction.whenever()` body. - ---- - -### `implement-the-view.md` - -**Front-matter title**: `Implement the View` - -- Show the class declaration: - `internal class WhenFieldView : View()` -- Explain the three type parameters: ID type, state proto message, state builder. -- Show the subscriber method: - ```kotlin - @Subscribe - fun on(e: WhenFieldDiscovered) = alter { - subject = e.subject - errorMessage = e.errorMessage - bound = e.bound - type = e.type - } - ``` -- Note: views only accumulate data; validation and business logic belong in the Reaction. - ---- - -### `implement-the-generator.md` - -**Front-matter title**: `Implement the Generator` - -- Show the class declaration: - `internal class WhenGenerator : OptionGeneratorWithConverter()` -- Explain when to use `OptionGeneratorWithConverter` vs the plain `OptionGenerator` base - (the former injects a `JavaValueConverter` for converting proto field values to Java - code expressions). -- Show the lazy view query: - ```kotlin - private val allWhenFields by lazy { - querying.select().all() - } - ``` - Explain that `querying` is injected by the framework and must not be accessed before - the generator is invoked. -- Show the `codeFor` override: - ```kotlin - override fun codeFor(type: TypeName): List = - allWhenFields - .filter { it.id.type == type } - .map { GenerateWhen(it, converter).code() } - ``` - Explain that the framework calls `codeFor` once per processed message type and that - each returned `SingleOptionCode` wraps a `CodeBlock` inlined into the generated - `validate()` method. -- Recommend the inner-class pattern (`GenerateWhen`) to separate "gather view data" - (generator) from "compose code string" (inner class), but note that internal helpers - can be omitted from documentation for brevity. - ---- - -## Style and formatting rules - -- Each new page must include a YAML front-matter block with `title`, `description`, and - `headline: Documentation` (follow existing pages in `04-validators/`). -- Use `##` for top-level sections and `###` for sub-sections within a page. -- Use fenced code blocks with language tags (`kotlin`, `protobuf`). -- Keep prose paragraphs short (≤4 lines); use bullet lists for multi-item enumerations. -- Avoid widows, runts, orphans, and rivers (see `.agents/documentation-guidelines.md`). -- All class/method/field names must match the actual source identifiers exactly. -- End each page with a "What's next" section linking forward to the next page and back - to the section index (`../`). - ---- - -## Out of scope - -- End-to-end code for a hypothetical new option. -- Documenting the `currency` option separately (it is already referenced in `_index.md`). -- Changes to any file outside `docs/content/docs/validation/05-custom-validation/`. From 1867f5de53d130c40fbc0ba636c72089bcb28ab4 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 21:50:37 +0100 Subject: [PATCH 31/35] Bump Time -> `2.0.0-SNAPSHOT.237` --- buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt index 05eb7bfaf0..7be4e8473f 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt @@ -40,7 +40,7 @@ import io.spine.dependency.Dependency ) object Time : Dependency() { override val group = Spine.group - override val version = "2.0.0-SNAPSHOT.236" + override val version = "2.0.0-SNAPSHOT.237" private const val infix = "spine-time" fun lib(version: String): String = "$group:$infix:$version" From 58e9382c34d0bcfa4f87875017037a7b6689d58f Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 22:00:10 +0100 Subject: [PATCH 32/35] Instruct the `/writer` skill to use footnote-style links --- .agents/skills/writer/SKILL.md | 5 +++++ .agents/skills/writer/agents/openai.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 2883a9522e..30cb51e38b 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -49,6 +49,11 @@ description: > - Follow `.agents/documentation-guidelines.md` and `.agents/documentation-tasks.md`. - Use fenced code blocks for commands and examples; format file/dir names as code. +- In Markdown files, prefer footnote-style reference links for external `https://` + targets instead of inline links. Write readable body text like + `[label][short-id]`, then place the URL definition near the end of the file, + such as `[short-id]: https://example.com/long/path`. Keep reference IDs short + and descriptive. Inline links are still fine for local relative paths. - Avoid widows, runts, orphans, and rivers by reflowing paragraphs when needed. ## Make docs actionable diff --git a/.agents/skills/writer/agents/openai.yaml b/.agents/skills/writer/agents/openai.yaml index dd8946abd4..de78e54453 100644 --- a/.agents/skills/writer/agents/openai.yaml +++ b/.agents/skills/writer/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Writer" short_description: "Write and update user/developer docs" - default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, keep the current docs sidenav.yml in sync with content changes, do not update historical version docs unless explicitly asked, keep changes concise and actionable, and include concrete examples and commands where appropriate." + default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, use short footnote-style reference links for external HTTPS URLs in Markdown, keep the current docs sidenav.yml in sync with content changes, do not update historical version docs unless explicitly asked, keep changes concise and actionable, and include concrete examples and commands where appropriate." From ed04195542732005719c2de3c30328da78739682 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 22:03:07 +0100 Subject: [PATCH 33/35] Use footnote-style links in Markdown --- .../implement-the-generator.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md index 80d2c46a7d..4659800e1e 100644 --- a/docs/content/docs/validation/05-custom-validation/implement-the-generator.md +++ b/docs/content/docs/validation/05-custom-validation/implement-the-generator.md @@ -50,9 +50,7 @@ filtered view is passed to a helper that composes the actual `CodeBlock`. Each `SingleOptionCode` wraps a `CodeBlock` that is inlined directly into the generated `validate()` method, so the code must be a valid Java statement or block. -For complete context, see -[`WhenGenerator.kt`](https://github.com/SpineEventEngine/time/blob/master/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt) -in the Spine Time repository. +For complete context, see [`WhenGenerator.kt`][when-generator-kt] in the Spine Time repository. ## Generated code paths @@ -62,17 +60,20 @@ the `(when)` option: - For a single message field, it generates one validation block for the field value. - For a repeated message field, it generates a `for` loop and validates each element inside that loop. +- For a map field, it generates a `for` loop over the map's `.values()` and validates each + value inside that loop. -Both branches delegate to the same `validateTime(...)` helper, so the time comparison, +All three branches delegate to the same `validateTime(...)` helper, so the time comparison, violation construction, and placeholder handling stay in one place. The difference is only -where the checked value comes from: the field getter for a single message, or the loop variable -for each repeated element. +where the checked value comes from: the field getter for a single message, the loop variable +for each repeated element, or the map value variable for each map entry. -See the full source around -[`GenerateWhen.code()`](https://github.com/SpineEventEngine/time/blob/master/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt#L105-L117) -for the exact generated Java shape. +See the full source around [`GenerateWhen.code()`][when-generator-kt] for the exact generated +Java shape. ## What's next - [Pass the option to the Compiler](pass-to-compiler.md) - [Back to Custom Validation](../) + +[when-generator-kt]: https://github.com/SpineEventEngine/time/blob/5268c2386f2dd4f400a7fb6885474c2945475b3a/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt From 986846ee161578081aaaea218705422fcf77b891 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 22:06:24 +0100 Subject: [PATCH 34/35] Update `config` --- .agents/skills/writer/agents/openai.yaml | 3 ++- .github/workflows/build-on-ubuntu.yml | 3 +-- .github/workflows/build-on-windows.yml | 3 +-- .github/workflows/ensure-reports-updated.yml | 3 +-- .github/workflows/gradle-wrapper-validation.yml | 1 - .github/workflows/increment-guard.yml | 3 +-- .../workflows/remove-obsolete-artifacts-from-packages.yaml | 4 ++-- .../main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt | 4 ++-- config | 2 +- 9 files changed, 11 insertions(+), 15 deletions(-) diff --git a/.agents/skills/writer/agents/openai.yaml b/.agents/skills/writer/agents/openai.yaml index de78e54453..44eaa4e241 100644 --- a/.agents/skills/writer/agents/openai.yaml +++ b/.agents/skills/writer/agents/openai.yaml @@ -1,4 +1,5 @@ interface: display_name: "Writer" short_description: "Write and update user/developer docs" - default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, use short footnote-style reference links for external HTTPS URLs in Markdown, keep the current docs sidenav.yml in sync with content changes, do not update historical version docs unless explicitly asked, keep changes concise and actionable, and include concrete examples and commands where appropriate." + default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, keep changes concise and actionable, and include concrete examples and commands where appropriate." + diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml index f8c24933f9..b571ce82f4 100644 --- a/.github/workflows/build-on-ubuntu.yml +++ b/.github/workflows/build-on-ubuntu.yml @@ -1,10 +1,9 @@ -name: Build under Ubuntu +name: Build on Ubuntu on: push jobs: build: - name: Build under Ubuntu runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build-on-windows.yml b/.github/workflows/build-on-windows.yml index 4e6b57f1fd..37eba1be7c 100644 --- a/.github/workflows/build-on-windows.yml +++ b/.github/workflows/build-on-windows.yml @@ -1,10 +1,9 @@ -name: Build under Windows +name: Build on Windows on: pull_request jobs: build: - name: Build under Windows runs-on: windows-latest steps: diff --git a/.github/workflows/ensure-reports-updated.yml b/.github/workflows/ensure-reports-updated.yml index fdd8b8e673..93531059e9 100644 --- a/.github/workflows/ensure-reports-updated.yml +++ b/.github/workflows/ensure-reports-updated.yml @@ -8,8 +8,7 @@ on: - '**' jobs: - build: - name: Ensure license reports updated + check: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 858cebbcc4..a7cde85e58 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -9,7 +9,6 @@ on: jobs: validation: - name: Gradle Wrapper Validation runs-on: ubuntu-latest steps: - name: Checkout latest code diff --git a/.github/workflows/increment-guard.yml b/.github/workflows/increment-guard.yml index 1993841a65..dd7df71378 100644 --- a/.github/workflows/increment-guard.yml +++ b/.github/workflows/increment-guard.yml @@ -9,8 +9,7 @@ on: - '**' jobs: - build: - name: Check version increment + check: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/remove-obsolete-artifacts-from-packages.yaml b/.github/workflows/remove-obsolete-artifacts-from-packages.yaml index fe8ad849d0..f706171007 100644 --- a/.github/workflows/remove-obsolete-artifacts-from-packages.yaml +++ b/.github/workflows/remove-obsolete-artifacts-from-packages.yaml @@ -34,7 +34,7 @@ env: jobs: retrieve-package-names: - name: Retrieve the package names published from this repository + name: Retrieve package names runs-on: ubuntu-latest outputs: package-names: ${{ steps.request-package-names.outputs.package-names }} @@ -54,7 +54,7 @@ jobs: echo "package-names=$(<./package-names.json)" >> $GITHUB_OUTPUT delete-obsolete-artifacts: - name: Remove obsolete artifacts published from this repository to GitHub Packages + name: Delete obsolete artifacts needs: retrieve-package-names runs-on: ubuntu-latest strategy: diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 3f4dfab758..2333e4ce7e 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2025, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ object CoreJvmCompiler { /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.063" + const val version = "2.0.0-SNAPSHOT.062" /** * The ID of the Gradle plugin. diff --git a/config b/config index 5118c00783..f24236f0a8 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 5118c0078339c7fe6d74d652921c42010cf64a37 +Subproject commit f24236f0a897e9d6a101e60d1dd936317c7a3c06 From cc28d99774f68ec718ad3c50a592de94724412fa Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 30 Apr 2026 22:10:36 +0100 Subject: [PATCH 35/35] Update dependency reports --- dependencies.md | 30 +++++++++++++++--------------- pom.xml | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dependencies.md b/dependencies.md index 0d1ce1e06d..5821b16736 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1090,7 +1090,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -1791,7 +1791,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -1805,7 +1805,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:54 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:34 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -2864,7 +2864,7 @@ This report was generated on **Thu Apr 30 20:45:54 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -3961,7 +3961,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -4015,7 +4015,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:35 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -4822,7 +4822,7 @@ This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -5511,7 +5511,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -5976,7 +5976,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -6602,7 +6602,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -7170,7 +7170,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -7781,7 +7781,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:37 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -8526,7 +8526,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -8766,7 +8766,7 @@ This report was generated on **Thu Apr 30 20:45:56 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -9116,6 +9116,6 @@ This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 30 20:45:55 WEST 2026** using +This report was generated on **Thu Apr 30 22:06:36 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/pom.xml b/pom.xml index 743612140c..07964fc1f4 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ all modules and does not describe the project structure per-subproject. io.spine spine-time - 2.0.0-SNAPSHOT.236 + 2.0.0-SNAPSHOT.237 compile