From 8f12cb68706590b88ffa1b123822f46d3addea3d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 14 May 2026 16:09:42 +0100 Subject: [PATCH 01/12] Update `config` --- .agents/skills/bump-gradle/SKILL.md | 117 +++++++++++++++++ .agents/skills/bump-gradle/agents/openai.yaml | 4 + .agents/skills/bump-version/SKILL.md | 118 ++++++++++++++++++ .../skills/bump-version/agents/openai.yaml | 4 + .agents/skills/writer/SKILL.md | 9 ++ .agents/version-policy.md | 35 ++---- .github/workflows/ensure-reports-updated.yml | 2 +- .gitignore | 3 + CLAUDE.md | 2 +- .../spine/dependency/local/CoreJvmCompiler.kt | 2 +- .../io/spine/dependency/local/Validation.kt | 2 +- .../gradle/report/license/LicenseReporter.kt | 9 +- .../io/spine/gradle/report/license/Paths.kt | 20 ++- .../spine/gradle/report/pom/PomGenerator.kt | 11 +- .../license/DependencyReportOutputTest.kt | 104 +++++++++++++++ config | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 48966 -> 48462 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 2 +- gradlew.bat | 31 ++--- 20 files changed, 417 insertions(+), 64 deletions(-) create mode 100644 .agents/skills/bump-gradle/SKILL.md create mode 100644 .agents/skills/bump-gradle/agents/openai.yaml create mode 100644 .agents/skills/bump-version/SKILL.md create mode 100644 .agents/skills/bump-version/agents/openai.yaml create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt diff --git a/.agents/skills/bump-gradle/SKILL.md b/.agents/skills/bump-gradle/SKILL.md new file mode 100644 index 000000000..e5d09269f --- /dev/null +++ b/.agents/skills/bump-gradle/SKILL.md @@ -0,0 +1,117 @@ +--- +name: bump-gradle +description: > + Update the Gradle wrapper version used by this repository. Use when asked to + upgrade Gradle, bump the Gradle wrapper, move the project to the latest + Gradle release from the official release notes, run the Gradle build, and + commit Gradle wrapper and dependency report changes separately. +--- + +# Bump Gradle + +Use the official Gradle release notes as the source of truth for both the +latest version and the wrapper update command: + +https://docs.gradle.org/current/release-notes.html#upgrade-instructions + +Always check that page at task time. Do not rely on remembered Gradle versions. + +## Checklist + +1. Work from the target repository root. + + Confirm `./gradlew` and `gradle/wrapper/gradle-wrapper.properties` exist + before changing anything. Inspect `git status --short` and preserve unrelated + user changes. If Gradle wrapper files are already modified, inspect the diff + and continue only when those edits are part of the same requested Gradle + bump; otherwise ask before overwriting or staging them. + +2. Read the latest Gradle version from the release notes. + + Open the Upgrade instructions section at the URL above. Use the version in + the release heading and the wrapper command shown there. They should agree; + if they do not, stop and report the mismatch. + +3. Run the wrapper update command. + + Substitute the version from the release notes: + + ```bash + ./gradlew wrapper --gradle-version=GRADLE_VERSION && ./gradlew wrapper + ``` + + For example, if the release notes say Gradle `9.5.1`, run: + + ```bash + ./gradlew wrapper --gradle-version=9.5.1 && ./gradlew wrapper + ``` + +4. Run the build. + + ```bash + ./gradlew clean build + ``` + + If the wrapper update or build fails, do not commit partial changes. Report + the failing command and the relevant error output. + +5. Commit only Gradle-related files. + + Inspect `git status --short` and `git diff --name-only`. Stage only files + created or updated by the Gradle wrapper bump, normally: + + ```text + gradle/wrapper/gradle-wrapper.properties + gradle/wrapper/gradle-wrapper.jar + gradlew + gradlew.bat + ``` + + Include other Gradle-owned files only when they are directly required by the + wrapper update and are clearly part of the same change. Do not stage + dependency reports or unrelated build output in this commit. + + Commit with the exact subject, replacing `GRADLE_VERSION`: + + ```text + Bump Gradle -> `GRADLE_VERSION` + ``` + + Example: + + ```bash + git commit -m 'Bump Gradle -> `9.5.1`' + ``` + + If no Gradle-related files changed, do not create an empty commit; report + that the wrapper was already current after verification. + +6. Commit dependency reports separately when the build updates them. + + Stage only generated dependency report files. In repositories using this + config, the usual paths are: + + ```text + docs/dependencies/pom.xml + docs/dependencies/dependencies.md + ``` + + Include other changed files only when they are clearly generated dependency + reports from the build. Commit them separately with: + + ```text + Update dependency reports + ``` + +7. Verify the final branch state. + + Confirm the recent commit subjects and make sure no owned Gradle bump or + dependency report changes remain unstaged: + + ```bash + git log --format=%s -2 + git status --short + ``` + + Leave unrelated pre-existing user changes alone and mention them separately + in the final response. diff --git a/.agents/skills/bump-gradle/agents/openai.yaml b/.agents/skills/bump-gradle/agents/openai.yaml new file mode 100644 index 000000000..6edf97877 --- /dev/null +++ b/.agents/skills/bump-gradle/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Bump Gradle" + short_description: "Update the Gradle wrapper safely" + default_prompt: "Use $bump-gradle to update this repository to the latest Gradle wrapper version from the official release notes, build, and split Gradle/report commits." diff --git a/.agents/skills/bump-version/SKILL.md b/.agents/skills/bump-version/SKILL.md new file mode 100644 index 000000000..7143c3e9f --- /dev/null +++ b/.agents/skills/bump-version/SKILL.md @@ -0,0 +1,118 @@ +--- +name: bump-version +description: > + Bump the project version in `version.gradle.kts` following the Spine SDK + versioning policy. Use when starting a new branch, before opening a PR, or + when CI rejects a branch for a missing/insufficient version increment. Covers + locating the published version value, choosing the increment, committing the + bump, rebuilding reports, and resolving version conflicts. +--- + +# Bump the project version + +The authoritative policy is [Spine SDK Versioning][version-policy]. In this +skill's target repository, CI runs the `Version Guard` workflow, which invokes +`checkVersionIncrement` through `IncrementGuard`. The task fails if the current +project version already exists in the Maven repository. It does not compare git +branches or inspect commit subjects; the checks below are agent-side guardrails. + +## Checklist + +1. Work from the target repository root. + + Confirm `version.gradle.kts` exists before editing. If it is absent, stop and + report that this skill does not apply to the current checkout. + + Inspect `git status --short` before changing files. Preserve unrelated user + changes and stage only the version/report files this workflow owns. + +2. Locate `version.gradle.kts` and update the value that feeds + `versionToPublish`. + + The published version may be a literal: + + ```kotlin + val versionToPublish: String by extra("2.0.0-SNAPSHOT.182") + ``` + + Or it may come from another variable: + + ```kotlin + val compilerVersion: String by extra("2.0.0-SNAPSHOT.043") + val versionToPublish by extra(compilerVersion) + ``` + + In the second case, update the source value (`compilerVersion` here), not + only the `versionToPublish` alias. + +3. Choose the increment. + + For the normal snapshot-line PR, increment the trailing snapshot number by + one: `2.0.0-SNAPSHOT.182` -> `2.0.0-SNAPSHOT.183`. Preserve existing + zero-padding: `2.0.0-SNAPSHOT.009` -> `2.0.0-SNAPSHOT.010`. + + For a breaking snapshot-line PR, advance to the next multiple of 10 that is + strictly greater than the current value: `.187` -> `.190`, and `.180` -> + `.190`. + + For release-line work, follow the [policy][version-policy]: urgent fixes bump `PATCH`; + feature work or significant fixes bump `MINOR` and reset `PATCH` to `0`. + +4. Commit only the `version.gradle.kts` change with this subject: + + ```text + Bump version -> `2.0.0-SNAPSHOT.183` + ``` + + Use the actual new version in the subject. Do not include unrelated files in + this commit. + +5. Run the build to verify the bump and regenerate reports: + + ```bash + ./gradlew clean build + ``` + + Repos using this config commonly finalize `generatePom` and + `mergeAllLicenseReports` after `build`, which updates + `docs/dependencies/pom.xml` and `docs/dependencies/dependencies.md` when + those reports are configured. + +6. If `docs/dependencies/pom.xml` or `docs/dependencies/dependencies.md` changed, + commit those generated files separately: + + ```text + Update dependency reports + ``` + + If the PR has the `License Reports` workflow, make sure the branch modifies + `docs/dependencies/pom.xml` and `docs/dependencies/dependencies.md`. + +7. Validate the branch state. + + ```bash + BASE=master + git fetch --quiet origin "$BASE" + RANGE="$(git merge-base HEAD origin/$BASE)..HEAD" + git log --format=%s "$RANGE" | grep '^Bump version ->' + git diff --name-only "$RANGE" -- version.gradle.kts | grep '^version.gradle.kts$' + ``` + + Use the actual merge target for `BASE` when it is not `master`. + Also confirm `git status --short` has no uncommitted changes created by the + version bump or report regeneration. + +## Conflict Rule + +When merging a base branch into a feature branch: + +- If the base branch version is lower, keep the feature branch version. +- If the base branch version is greater than or equal to the feature branch + version, set the feature branch version to `base + 1`, or apply the breaking + change rounding rule. + +Do not require a completely clean worktree if unrelated user changes are +present. Instead, make sure no uncommitted changes were created by the version +bump or report regeneration. + +[version-policy]: https://github.com/SpineEventEngine/documentation/wiki/Versioning diff --git a/.agents/skills/bump-version/agents/openai.yaml b/.agents/skills/bump-version/agents/openai.yaml new file mode 100644 index 000000000..12f6e4f9b --- /dev/null +++ b/.agents/skills/bump-version/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Bump Version" + short_description: "Bump Spine project versions safely" + default_prompt: "Use $bump-version to bump the project version in version.gradle.kts, commit the version change, rebuild dependency reports, and verify the branch." diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 30cb51e38..6b9d86f88 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -49,6 +49,15 @@ 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. +- When referencing a documentation page or section in body prose, use typographic + double quotation marks only if the visible reference text is the actual page or + section title, such as the “Getting started” page or the “Troubleshooting” + section. The title normally starts with a capital letter. Do not add these + quotes around generic or descriptive links such as “this page”, “the next + section”, “declaring constraints”, or `4.3`, even if they point to a page or + section. Do not add these quotes in “What’s next” sections or navigation + elements. Keep file paths, identifiers, frontmatter values, navigation labels, + and Markdown link labels in their expected syntax. - 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, diff --git a/.agents/version-policy.md b/.agents/version-policy.md index 65dc45749..95ac4513e 100644 --- a/.agents/version-policy.md +++ b/.agents/version-policy.md @@ -1,30 +1,15 @@ # Version policy -## We use semver -The version of the project is kept in the `version.gradle.kts` file in the root of the project. +The project follows the [Spine SDK Versioning policy][wiki-versioning]. +The version is kept in `version.gradle.kts` at the project root and follows +[Semantic Versioning 2.0.0][semver] with Spine-specific extensions +(snapshot `NUMBER`, patch, and flavor suffixes). -The version numbers in these files follow the conventions of -[Semantic Versioning 2.0.0](https://semver.org/). +PRs without a version bump fail CI. -## Quick checklist for versioning -1. Increment the patch version in `version.gradle.kts`. - Retain zero-padding if applicable: - - Example: `"2.0.0-SNAPSHOT.009"` → `"2.0.0-SNAPSHOT.010"` -2. Commit the version bump separately with this comment: - ```text - Bump version → `$newVersion` - ``` -3. Rebuild using `./gradlew clean build`. -4. Update `pom.xml`, `dependencies.md` and commit changes with: `Update dependency reports` +For the bump procedure — version-number selection, the commit-message +convention, the rebuild, dependency-report updates, and conflict resolution — +use the [`bump-version`](skills/bump-version/SKILL.md) skill. -Remember: PRs without version bumps will fail CI (conflict resolution detailed above). - -## Resolving conflicts in `version.gradle.kts` -A branch conflict over the version number should be resolved as described below. - * If a merged branch has a number which is less than that of the current branch, the version of - the current branch stays. - * If the merged branch has the number which is greater or equal to that of the current branch, - the number should be increased by one. - -## When to bump the version? - - When a new branch is created. +[semver]: https://semver.org/ +[wiki-versioning]: https://github.com/SpineEventEngine/documentation/wiki/Versioning diff --git a/.github/workflows/ensure-reports-updated.yml b/.github/workflows/ensure-reports-updated.yml index 7df73d220..315cd202b 100644 --- a/.github/workflows/ensure-reports-updated.yml +++ b/.github/workflows/ensure-reports-updated.yml @@ -20,6 +20,6 @@ jobs: # Check out the `config` submodule to fetch the required script file. submodules: true - - name: Check that both `pom.xml` and license report files are modified + - name: Check that dependency report files are modified shell: bash run: chmod +x ./config/scripts/ensure-reports-updated.sh && ./config/scripts/ensure-reports-updated.sh diff --git a/.gitignore b/.gitignore index 5f85d295e..c43c66220 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,6 @@ pubspec.lock # Python cache __pycache__/ *.pyc + +# Claude working files +/.claude/worktrees/ diff --git a/CLAUDE.md b/CLAUDE.md index 72e0ad227..38753d02a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,7 +3,7 @@ ## Agent Guidelines Please read and follow all guidelines in the project's agent documentation: -- Start with the table of contents: `./agents/_TOC.md`. +- Start with the table of contents: `.agents/_TOC.md`. - Follow all linked documents from the TOC. - Apply all coding standards, formatting rules, and project conventions found in these documents. 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 2333e4ce7..0a52ff782 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.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/Validation.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt index 121d7aac2..600deeda7 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.414" + const val version = "2.0.0-SNAPSHOT.415" /** * The last version of Validation compatible with ProtoData. diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt index 8ede9919a..14c280b7d 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.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. @@ -43,7 +43,7 @@ import org.gradle.kotlin.dsl.the * * Transitive dependencies are included. * - * The output file is placed to the root folder of the root Gradle project. + * The output file is placed under `docs/dependencies` of the root Gradle project. * * Usage: * @@ -146,7 +146,7 @@ object LicenseReporter { /** * Merges the license reports from all [sourceProjects] into a single file under - * the [rootProject]'s root directory. + * the [rootProject]'s dependency report directory. */ private fun mergeReports( sourceProjects: Iterable, @@ -165,7 +165,8 @@ object LicenseReporter { } println("Merging the license reports from all projects.") val mergedContent = paths.joinToString("\n\n\n") { (File(it)).readText() } - val output = File("${rootProject.rootDir}/${Paths.outputFilename}") + val output = Paths.outputFile(rootProject.rootDir, Paths.outputFilename) + output.parentFile.mkdirs() output.writeText(mergedContent) } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Paths.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Paths.kt index 975a73b0e..1d1632752 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Paths.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Paths.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. @@ -26,15 +26,23 @@ package io.spine.gradle.report.license +import java.io.File + /** - * Filesystem paths used by [LicenseReporter]. + * Filesystem paths used by [LicenseReporter] and + * [PomGenerator][io.spine.gradle.report.pom.PomGenerator]. */ internal object Paths { + /** + * The directory in the root project to which dependency reports are written. + */ + internal const val outputDirectory = "docs/dependencies" + /** * The output filename of the license report. * - * The file with this name is placed to the root folder of the root Gradle project — + * The file with this name is placed under [outputDirectory] of the root Gradle project — * as the result of the [LicenseReporter] work. * * Its contents describe the licensing information for each of the Java dependencies @@ -46,4 +54,10 @@ internal object Paths { * The path to a directory, to which a per-project report is generated. */ internal const val relativePath = "reports/dependency-license/dependency" + + /** + * Obtains a dependency report file under [outputDirectory] of the root project directory. + */ + internal fun outputFile(rootDirectory: File, filename: String): File = + rootDirectory.resolve(outputDirectory).resolve(filename) } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt index 6e986e136..7ffeda189 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.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. @@ -26,6 +26,7 @@ package io.spine.gradle.report.pom +import io.spine.gradle.report.license.Paths import org.gradle.api.Project import org.gradle.api.plugins.BasePlugin @@ -41,7 +42,7 @@ import org.gradle.api.plugins.BasePlugin * The generated `pom.xml` is not usable for Maven build tasks and is merely a * description of project dependencies. * - * Configures the `build` task to generate the `pom.xml` file. + * Configures the `build` task to generate the `pom.xml` file under `docs/dependencies`. * * Note that the generated `pom.xml` includes the group ID, artifact ID and the version of the * project this script was applied to. In case you want to override the default values, do so in @@ -63,6 +64,8 @@ import org.gradle.api.plugins.BasePlugin @Suppress("unused") object PomGenerator { + private const val pomFilename = "pom.xml" + /** * Configures the generator for the passed [project]. */ @@ -81,8 +84,8 @@ object PomGenerator { val task = project.tasks.register("generatePom") { doLast { - val pomFile = project.projectDir.resolve("pom.xml") - project.delete(pomFile) + val pomFile = Paths.outputFile(project.rootDir, pomFilename) + pomFile.parentFile.mkdirs() val projectData = project.metadata() val writer = PomXmlWriter(projectData) diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt new file mode 100644 index 000000000..9c36b218b --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt @@ -0,0 +1,104 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.gradle.report.license + +import io.kotest.matchers.shouldBe +import io.spine.gradle.report.pom.PomGenerator +import java.io.File +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.BasePlugin +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +@DisplayName("Dependency reports should") +class DependencyReportOutputTest { + + @TempDir + lateinit var projectDir: File + + private lateinit var project: Project + + @BeforeEach + fun setUp() { + project = ProjectBuilder.builder() + .withProjectDir(projectDir) + .build() + project.group = "io.spine" + project.version = "2.0.0" + } + + @Test + fun `write the generated POM under docs-dependencies`() { + PomGenerator.applyTo(project) + + project.tasks.named("generatePom").get() + .executeActions() + + val pomFile = projectDir.resolve("docs/dependencies/pom.xml") + pomFile.exists() shouldBe true + } + + @Test + fun `merge license reports under docs-dependencies`() { + project.pluginManager.apply(BasePlugin::class.java) + val subproject = subproject("sub") + LicenseReporter.generateReportIn(subproject) + val sourceReport = subproject.layout.buildDirectory.asFile.get() + .resolve(Paths.relativePath) + .resolve(Paths.outputFilename) + sourceReport.parentFile.mkdirs() + sourceReport.writeText("license report") + + LicenseReporter.mergeAllReports(project) + + project.tasks.named("mergeAllLicenseReports").get() + .executeActions() + + val reportFile = projectDir.resolve("docs/dependencies/dependencies.md") + reportFile.readText() shouldBe "license report" + } + + private fun subproject(name: String): Project { + val subprojectDir = projectDir.resolve(name) + subprojectDir.mkdirs() + return ProjectBuilder.builder() + .withName(name) + .withParent(project) + .withProjectDir(subprojectDir) + .build() + } + + private fun Task.executeActions() { + actions.forEach { + it.execute(this) + } + } +} diff --git a/config b/config index 69c13f454..c3ab20a7f 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 69c13f45498b638ff4eccf8e097bb88c49af96e9 +Subproject commit c3ab20a7fc8cf1464ff45651a19935b5b7aeaea7 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d997cfc60f4cff0e7451d19d49a82fa986695d07..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch delta 39760 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 e(*aw}>J`&Ep$5$o2mVP)i30-M3~u@DBh0 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 delta 40115 zcmXV%Q+QqN*Y%q=&W^QX+qP|^v2A-dHg{~BZLG#@Y};vUetrJ$cd`!F*}CSOW8U}p zjWzl5b?D=3y%REMd38|-8Qy^Zu()|mV!mA+wfMNA46Czj#TqxuEX}M1)`V!F<%-^p@5zZ#zK3v1!%Twk#AfK(j(D~g%G)1wvnIq8OF~L7?k0+oGmA81 zTNa^TfZQ1UR18{GD#XxwYlDgr@y$#z*v;V_$-=XmV<%aevua^h4}Kt39J%8 z01n7S1|v};gKz6GLFQ^}is&jB`r*w0`*1k~iK`%P11mW>35O<%ZWJbRi5Lo$7^X6| z@RJ_5(u@qW*u>9iqAQJ9>FI%|bI&LA?g(FK8#ynYS61J;rm0oSfcN)@Z$12}IGaTH z{7l^{HhLTAsinyn?oy*Pl^a&Ll#hV5F)lj=!wPMF8DAXEDK zEW%lnet{1f$Ong_!u|Xs$?aU5BrOZJCX1tCW@A98mQnx=V1JAImQg>m=j2PuG!& z+x>zNl!Fl@KPf6~ZxqNyH?Y zgzvFL#QEOI`NKFF03>De3R@Hoe2uXUZ~YMJc8;M58@pq<0bDZYQaGmWP#rFI8__>v z%IOj*27usU$K5DrT!U!tq$8w@5kHKAB&m1tKX9Cb=*jR#RbQoutpHHBp&tcHQW`Jlro>kEh7FGfGGAU;z<0vB|C+GeBXqf5CW1{Jko72_Lr`~dxDw1OR zYFy0;ZPw}@$9BT_b>YkaBZQxHJ8*>XE{on)I_}0Gj=oeP+$xTXP?J$B-qNY8(zUqj z{Pw=TDd8a+gQfvB2i|6|XX~AEx+DZz@CAA&3GPbe2`-6Ut$vPeR;wmcPu%cB`HoNw z=8=}QRKP#NJWx{3Yv@qpt_Wv=m#)@nG_we`q6LRbHi^nv6COJSVch+*!m?U+68vbm zlE5U;B3wu>bspSw;96NULIncfV#|JNGe`p@mT2RyGTFMCW0bCmekexPOg z)pLM`G7s%(Zm==0+U9|8=eF|+MsuOZl_rTZVyHrj?MG1ph`^{ldYpC`}`&LK2r^$xC> zPG>xZp_<}2HY@zeM|*{C;u+>*^6slSSBxypdd~!8t!bQbC9-gEFUnrCzr`2Ms*b)W zt1qhQ!$EOttzzGr6zgJ)JM{-7$LrwlqqGD9WpHU@H+jioEn*tEPk56WEv&erfnm~S zl&&eusI$+G=}%D)Tr!`bgwzT@5pkpZFP=-C8jf^}Az704gjm!@2~0>oIXy$h4S9Kf zHO3{#d)yfIE)dczjb~13L^wz2K3{74x%SPB^2z%Ik)MSSiJmk4*Jz7!B(l0-VJth4 zt>c_Fia^W6VvX~Otv5q`aVfh!8kaDM0$ys#-y79&v5=Z%(m;powSoEg5sy`Uk|(08 z45BY+c3N)Ueqy^)41CRM_HslQ*k~(}Qvz(BlYiE)(?DDPUz{Mga18i;J?gH+Yn=SQ zFKf8VE~L#@SMq80Gq*W%H(T&%h_upy^-2cK?LYQ(y;aB7$O=qWSE(#G(Y$#7NT1 zjB4v!hI*W1aNvMI^Sq9Y#tYAFW6HWbMCC^m=KDL8*1v#gxLv za4W1r5E51)-unT7gTZMh$Rc%nr*;S%One^p3q6u|SaP8dlD2o|W5vZ&r9;hf5zcJ% z*OBOuKDLzp%aj``JQvRj?Gynb!D)#NAp-~~wx)62TJDD!6T7V6A+mV}``+tzs;D~w zgxq(i5NQ?!98koJx<}a( z&eEl-bX1hYbqiqaSX1n1Dy6XdJSA$kD!JkpyKFH}a0)c8PXU|Bby*|@o6Td6&f_p~ zEJnUz?ZtM}PhS_K8ar3M?jzk0oo~Q1qz2Jon9J{?^%oFyiWf4C1BdHgaRgjDFuXE% z%-;YjE_V;KN&~No6uF=jmf$TQ+AsDiYu=g6Zh4r$f_0F;I;fbtHyUaC5K-@D_`$~< z13s@o*OH&BJ^!AMws&qAfA64@-FR-XHZsm}eGJH+?h4XsBkqxT6Y{shPuu~?jxg2z zj??eZR6ju??;!;@HB&&P@aYlPyE-!c(z1p>MR&N9T{^>FJZ9H~*yFB$bt*ul?_2@j z`%~i2(&4_}>Trs6R@L<#*Ukw&mkKyuu7mOc13JBl&^nh6pC~@lQyQGqfp3VR9t6w zI4h?2@LD~r#O`wZ6!u*qO5jvD_LW2%A;zA|%5d$zWw>m~y-kbXdL{c|s;zu=nnGu( zn}-~CeC?d52)Pz?Jp(f02DYf;^v~<}%Sx$giY7Q9s2#^`@1$Rd@0yYdUYMyV*4wLd z5uhSjBqb*2Mgmob#mI*52d>fgn{*LwioVa0_u|Zvg|}gz6enW!an8!9Qic4T#yY8) zkjl~}gJVJUD?p3-`uQWJX>XUM9|K-d+v6vN^%9J34fWw8 zw~-^`VoG|OXY5B+hiIr$Zz4yi+=#t${&zy}o)m4&M)6A(CB=OG-mvViP`#fdnP3u6 zcfSm6(E+wZYP4eMP_kZHD+4^8L|3$x5C@CUA|u_qLcxL-GyQ(MlSX@gS{v?Kvt#Dj zFkjW)rZD&Ru4Yh`t#7lSCvG^#6Rsi`l6Co@rjWXwT75}~A*}~Bwc`lO;;Tw{4(^4R z0w!&}t{6q-{wV}vfcm(GaBx_x3+8-1T-Je&5Z#=t=jy1PY_UF+K4KeerVsB*60*pi z&m5{YodqqfcD&E#>9%^9OdSpDy8UU~TrEjRDJ?r`wGn9UW+%a%T%DawI=F$YjGdo4 zL&r(G*PpJSihFE5>s-b255j^qBJxga9@!q`qYr6s63s&^XumL`<8SN{2Y(DF&a4NM z@Ex%!{P!$m&T2M%T){waK-dcyKJ$X{yL4~GxN~B>$`JwKL4L)t=JZSkQiS?Y4m^e^ zFnvBpcnH)?851H6DapW>aM>Lpvk|Db2aQDFgv6n>i(x97nmp+zq({1?lma_q!-yEa z2Zu8{M`xm4j$23X`*tj<-aB*Au?=~DzXSWq;5ca3 zsoX5vG79|{KNAajig)6m(E1|R!ldkPM^e0Gd*f1!FUD{!M3-p=_lD<|M;mm>%k!Jn zH*-T7J8VMC^DS9B88F3Vo8R?gR3yHNuHqdS(W)@eF59QnYP8xkb0$~kMFZ1heNUApov~(>x<{0N z?_iiCr}cgxC48go)`)f7UdtYhyUw1zio*Zt{^WpJYvO6_X02ljKME|*=^Z^Kc3 zhF}#TZPW2eSBP-%66mX2Tn!g?OW=*&73QM(<53{`8h&(`~To4Eb{EPnOjr|Oj*L)?7j z8{I*cT)X{gMD4L7`pn)rcct6 z@h2go%-&5L@y)?RE~jd1q?);MxIv3@GbDn3SFioUuk9xLt8;s=-w0zR#>GJ2n&LS& z`oOox?`o01KKgK)(snq1<0(HlLVE59)Y|TleI1Ilq#Zjcif^6VHX~d@v4;PIiE_3- zI{e@PD2nO!DATjYFHamXIC`fg zZiaIJ%6}ibj9qQdW24QnK|}_S<8{ui)i?zoR!pe?`2oW1$Qg6KbB(Bs=Q}Ky7={_ zZ@){1ohh+`E8A4(&6ZqGdFhbM?&R-f8TO~*ldFq>(gCRG=G!>Vh13G6k8*)}q2eG5 zX4qH0s_C^o!)l5tXkTBS5uz5CCTD%wx zJA3ax&0hJ*B?zI^ZWrXockqMQ|L7ZEgz7WRYkIcgMO7{~AjW$Fs1{q-YF(?kMVIqg zwlCfE)9`l~xg2Ixq}LKfA>$fHJD7qJOcH-LxLH;hYnvGQ=6y3AX?b%nilU061D*yL zEuH_+tu4=}RnvVGh?lF9k)85Ji8A`gKo28tTZvQy0hencq(pnE#qL4U4qL@B^kR!j z>cG@2DVJ9WWSVN!&s?&cW5NEY0JOjxtp4(k#?BH_WDaB~bXpf6(J+t_CyEfXh6RaO zIKe(;-Sr%Hd7C+2qHinVY`-+N28j9~rpJ>@!B<4TG>izc4)WqJ%nhTpP#)W(pJb7L zx?y_$OL8OjHMd&3B_1@T>Xdlcp9cB2m9+4=wc=CiuHRWhZ%B_Y%}HX=TQ4HIngzqJ zZD+jKQ3zLaVJ}o>w%GdIM^uuw)|4d9J2*4ZOvp$u6-!J~3HP^ROPDdMhof6pGpxZ) zGaa+Ugx2u|>*@^sv_s5;H-=Tsv^6wNOacRa_oQEc#h4a`5Sd;;=`lk|K2Oj9afvGL zT>ts7c%7_mD!p7|1(3!o6@$%;3hQ_Na{q#CQ@CxO>V!9aas7$9D}R`V&ooQ|E7qs5 z$zKdn*}9jE2JryMjIIBA%{G`lU;gWtFKJ-oTmo=2xgIFGxlvBU;efOljh%cMjTr4s zthA&5XMG37FWnHJH%eYFkxeUk=F=C!&u-TYw%?y`Pf$h}u7JGw zIL)6#3#3b)p|<5}mE9$!8eRM6-#H&^lB#$p#84PR8Ct1ES|9^cM5V_$;5M*li?l;5VWgmc zWs*EBC?OgF5*JjzpYAr9BZNgJv9p!#hcU3W+nXQC#r5l(Mkc4W-G#(pKLlmF618>; zYx-XxKs5nQDkqotPjPVXKQW-)Pr`b5Hd;y9CX_*!j=BbZ$4Cz~P7uCBE)1a59fd;P zE}y6U4r)>>KoQa-&;~sk0!2a&65d3GoftoT6{v$BppBtken*FBdMr)<_sT!Fq0QDp z^L<;d6nu%3Jdm9>53PpSsuaLQ1K%FG>y&rc8@&s`OLi>6)Kt5FD0flvRMjz1rRjIi zUk>{2zi_HGJ?x}}|CDr8{M{qu%vW8T!fmDHTwYN&pw-O#!wrKrH|!eHIjorv7Cx6H zq}6!+SiYc@%q@=>UE=E~Y_93HheZpmN9Hb`a~Zeemx!_o>#{`sdGhD$8Ay@+RB93@J*2t80Vp91R- z4G9VJjbq_G#a|y4IoJ#sPEJ)SE2NwP4*#GBN7!5-@3eQXpS%voorY?Sewo)(Xn-n? zg>_Atao7 zf(&*o;#DwI$NsJB8D`5oe2CBiu~Y7<59$$K-)J2yj! z>vzDn9$znt?bDkZ-A^eLQx`>EM2{ddjA|&E#5C0ca46C(Da9coji?-b+)UVLdXlT| z0p~IZr{HHJR`U0BQykgL1{_D@_NIxp($Bg8b(+Bt0@J53vDu7yammw|6*!!65X+V! zsAUsbEfW&^mKd@zT*4gfg%lIcOE;Z<3{Vf@*}6au)$0-PIl{d%jkGo z1>y8->ZDUpqG*nU)HM!8EtCSp1hT5!qlz%7W{vOr4{xveFsw$X`3{&aFem$4hnm#` z%SA$WhY}@v{mfs)3;Vs_&>r4lqOR5{V2`hYo-m#pfE>e~i(mpioHqYP&szSzlRL6n zdQ2VzP5*doCZm}wd`)lpL!hZdXB>3=i4uB9Ugjk>zJNJKOzcA5)zj*koPk05XdMpS zk;M%<6M`4d?KW43hY@P&xCK@Gl#zGTl)%(?j_#v;kN?%{Ufc4f5PU&n22w&7!4Q~R zbg0mkL(qo8)K^}ewKr9_>v*Oe3loFM26iEt?`uo;YCH*e_M3Y@WT7pn%pOrmvA$8Is*S*jXVf! z+$)H5V#NL1bK``_?nP8Fp#J*Hu+rVfrF4KxR0zdOCoEnys{= zWW=!QJZTg!GhdIpbmJ0ULaxbq51D42H4SIfP<{+O)x`dJ3`QdziGYwXK-&nBIrAS) zTQINmi4RiRr_KADh}K8~^DZmA;fhMUv4f~>(-Q4rL&C6zo@x1;?`N?iUugoo!yA== zckcv0mUFcHwurDRNMun7o}g%QfJ>d;m?yQh#stfiMaS6at`=9!0!$fHFdb=i-0Xdo zhvqkxa{-Ag=hm{A2AH>vt%7A2LxvZFo zo9@urLefPA&me!23+SAX)4%_(VH*9d6iEN=FAMMwbOLZDy$EPa4o?JS*fLgzJu_3& zKF^QGPg-`esBs~GSF9umMLp(edu@GV|7Uv(-UA~9>buw*(~J65uTYPWEH7S>2CSD8 zpHTO&ypreeaQ9elzjI~mtMVn^lghPvNC?r;8UFNWk;&ossldbHA3(U>jo0%xXb z0u*>>U$!1J2T^8pfw6je04Npsl4JAjKs3XG!D7{3%czTsj$D?-FZ_bl+aE z4?noO4)-~q8#?h+t1P!Vz%*utIQXhiEEn9ldFfjg) z9%Pi8Csl)E8G}JjP?#}rNx)b{pbB7MVEw`%HFTq6VuQgZhCgCz8(9IK^5Z`yxMxSR zN-Wc#m(#}fn9alX>v8FlNC;wmkiP%u{T=%t-X}%3Lv-~e$ieS}iwZy?`?GF*ffmM`J=A93`@f_N@8j#>q{lsq+_fxk5#`Qby> z)>|c{{y!^FcKgQ)Y*ae++SO?BnwRHmhImlPXe}OxqL>5T|U`7D7mwq-gw1WAK8D` z77!h=Nt0EP1a})0GZ%~Ww=Lt%Ob2f+x;s=&BBFGA0>__WiZXy zMC})*Hz~Q$3iTPS$`wwPOe|f>CXV+IG2q-(o10A?DHV=l5#B<}DLu|6=hIvEasWTY z^=5nnU?Vfz;2XW+O%Lez3@-i+_!(oTbt6aT35Q;z*PJ4UiR64>OD7OoWq(o&J{kRe z?5&DlGz^c2hZrMhn@|Yt&@B*h-5z)i>@nh`ATK94&0Y@6%onGJjr^a#84nvJ<@!%C zhX0*->Hm>cOmI63Do9rgT?@D|3`sShO_9%zWu9pR7nne6fYLb_k@6J|ZCJ+7SmheJ9;%lZ-W* z68hxt9hwb$?`(Q45}utpvKn3-C2jbYrBg2V=RAEn{%5b2HtmYg9e>oQtZ8GCEMSeT zB_pd{`+lC#|9rf^d$=o6XTb*He>H{0{*ze}ahAylG@GL>k;Bq*Pt{=8)kX^fz<`LZcybrpRAi6Jh+qd3>(dH&gg9A}rxXrYi-OfmGa@5 zaawD$Zd+o$_h)n6d=Q3pVY7?5GO|FT<+PA-{|GjgwgQ=0t@e@>@Wf!TOQ7_ANV0D; ziyeQ}-=N~I_nxa(oRp1V!Pg9}`7Sz9&1dM;s`jgE??UnlVzrMus0cNrVXDN=(n@Z< zf)b91MLM8e`G;y`qC%RVJv6Up3U?6t6L-ws zaRhOQ9e|}|t{N%+4F_@P9^rrNGw{x^1_^Lq3Oh?n{@mMSsA(!s(Eb!%+JTptDa}mg zQzh86tt_GnvBl`anIruKPA(in59Ap;z{OnAOzV4%$ zQUh`8{~p~@343@;Rlv2MW$0ay_>ReztVJ7mqdwt2tI{{&*zMvkXeo(bLS2qJ>;a=Hv027+05D{i@6JD^Y#z|SLL?UGS*_alLv&N zfR%>oADp^-#fn4$5r*N8gj>_FjV9SXYRD0cr#yU0gr0}Cl z@BJ3>=iqi-LR5dCc0CG#e65ic4jJgT{jVdBhIso`s;I%4TFiC*q4%Y2IqF!qcRbV5T$3FJERMVE+eu{>z%2 zaZ>(I4KY5D;1q#hGpVC=WMfDZ5NHa4ZOdN=3(1XQQzTE!lX&31fiv7J`eqk2w6s1Y z-oJJ4>R>c}xA#94)A|%9dS!c=E`8k(k^wJpBD+4EucSHVz5mTI{CN40`l6*`@h=bk3pUEQ{BTBA9TvK9AF;!&I{E7=P1q@FVQV$#WCAo-ehJhn1jYnthmZMA@DN=Un#XvK=oLt%W zncf}Wqxky%>_WMwRY_UAzcrZBAZT{06FaH`*q<4+Q*X!%R{Cz2vn{M96~g9B2}t9G zjGSU)g|2qF{aDGLaQy7KDYBDTKwo}i6W-nB<5{j&Q>TagVx5Ge!|CcfWnk*$II*v+ z`o<3jfrA|)Mf4K7ApsqK=p^SXFGo!1806Pcn|E&lnI=)H0u6fggxdq4HCV*fmg{VY zG4(~b$*(xM34>~>nrk<_uqBPaf1J!09Ir<1p zs~Qzi84Ng!m3n7blW4_Cp&gc&nncP*#5ZY8*Pdt}DsL#(3MQw&Eg% zwZ%PNeFCNIolzGe3JA1k6Vda;P_svvA+y>ga;E5f_6SpnEWLB4LGxw3I+Z=g5dyf( z@>KaZ_z=oe{3CEKp2CLu?7FPn?6`vzs|x^sn(z-KtBG~&&L8pZBr~6(S>!rgfa-NcRueDRhUUHkBnuk#P3~!eZ2jL5HpU4J&Gq;m~sZ707=Uhi_!O z=Y8;q9TF%~cEqfQ`KKV-Nv2W#is~y`sZ90sEc$Q4-frGE$8vn^oKa<*@skCyb(g8G zKKn4U;yCLYzv4vJYkKuA|Jo2aSLU!##3;l``s zOM?ndvqGLvUx|Rlm+aW?{J-2X9C%HWjfljU8XDT;<=n_0SmdQ{mXQ-?vutaX#|RYN zrBpxM?kEITs<00G%buzUtwO)L&+rfy+L9)$EV&g`2^H}*YC$V?NTh3MaUV>Dk9?x6 z2@wQde5>kF0&YV7ti-^chg$ldM+sDjK?(k+QmX$aQXPDJpmEuLVGIG&`FWkKcK5_k zD{DaEVj-z?Z)|8((DVU&n?jjl5-S)({7&o*0b5o1>f0Ojb@TS*#6jobddBm|^V3(S z5QV^{w6wJ$@KVaow~|E9(@=rB!_e=n>N?;ltO@$K0K!ysuaUJeB3rgI!xS%87&_(*??mOgkRotrNk#cqmck!Vc#znl>@K;2k z!o)|tAQb8?^H8`GS!=ZAQF;6UNy*G0<*IE-TsajsIkXH(h6`I{DlHEdx_aIOG90Un zA(0Ngd}dD;CT5|ec4E5vh0GU;S~n-_e{w{VmFK*zqgO)v8^y`v@q&)pv;lyP85j$? zVN4y@7AXlr!@^7 z06ZE-W3&oVR-2d+7p9S3M>uHNhK&mJH=}8EP)lFHsyS&T{OsEZF+hDu*9FzJCkCQ% z>7&Nak44hfQ2-(v>ZXUI{a;5HPx2m_78Jmc4xv;Ya~j&_{b+qG)vPkZp?j+INsm;I zfK`fB29vd@*GW{3;xHX!xF(jr;QgiKGCwhlc*<#t8LxY2uehMV6xuk3- zjK+Va=;0o}xl2_xuSP4q8%U|wMRa1HG0rS3E)RE|Zf46a;D9Y!2C zUvA{KYVXU*ZdJujWkiyVao1*I;Y_fU9b~(MUfPR0iUS&i8r#$Lf%X@IElZXxZg4dU zX2ks2`V~iGXd{^2+6spVE>_}VWWr8Ra{C{U?D~;>qR4D0muRTY3Qydk@k3As8F9#l z>faKKaW)e_3tSmPxY0!V6p|_|$KB+Xd6c828RnCW@`fF(U%&R-7#cnT*xcuxpUf7W zDq?(b`jq%+kBg*IL7`L#8O>*@N7y9jqC!Y+CPL7_)uN2=tl>e+;r;56!9Fq;MyLcr zdKQX`ZUzH#v+u`#^Hg{6v&po$F(l3l2s@CP9=qI%s?^bt0NcW7HmwuFoyUBwo;n0^*`S0=v1|*g_JEpLD7J-v{`;lu0MtN{FC)N z)f^Tp=&6))MGSY#KT1WB3%4J?)GbhRizziqSWX1a%*j#9#Ygdn1+|jTqjE^ms+oDGcuu93~NAn)Y~3vDOsE5@;!N^~k`rRF&{n-fpURc^>DP zPMxDDwFPVuE!UHsOmIqpIVzV-%`zfbrkzHNfqcDwf4kiMCVF>XEkKgZZhteZ z>(Jmn-r^S$f1oQ!vWOn0(MY9-nWySnX5BRRwP1027~1{|&F~N|(N~B9KAt zi3V%)6a?bW?rlC7IR8U*LD&_H*>It*^sOt{v!Sw#&mD%#bfc&{pNl9@vNBNM`5f?Y z0OL#aL-T<>j=zT`j9%Z+qBSFIj%jlQyW-*-*P;f~& z03L>X;xO;}3d-C5;{w#l5nAJ!^?Rs0x3}aC0D^zcjo#U17yl;tEv<}2a=q&L;j&== zSD-ed-Q#9BAL3;}UNX}$zQk}l2t^ImTw%;xSOnXD4vxKHj03X`!iP6Z-}=-XupfV&wn%)1{NC8&WaT3%NNc6+eWXV`k<0^f19Fq ztU^SUXxajJMRnMyG4HNi?zQTr)TOv`ly{VXAFfKUNccQgDm}}zyFG*!o}0e67VNLL zOD&gQkgk=t=O!kyPQr;(ZA3WX;=XYpJbX*B4CVYe+lKgypJYgmm7IWuCyi0vRTV+Y zl94-CX3t89dX^b1QH~d1ifI|$$}*apk<%FtY8BHWSADHgYFb#R5VrKZbcx`&g8^MJqX5lscuU#9 zd_MZNm4BLgCNIVp#gA0vMwX{XHz}e&g9X;nk1Hq%Owixm?TpCl?;T+Y)1oZK37a9? zEjwu;J_~oZxYGvLh9JrAyfP1~h1vU^{d!g+9D6+--=NM~YVzYr-uQf6q#R>FJw%{E z|G=r^?hf<%aZ;kSH{4a@!1$Cc{4YB!Lq=ux3(w#FtJ6y9_(2Z%Lfjg3LTUZOPnniT zBSv~oVK%~Vp;_J9o`uprq0uNxhche7ZEeC~*)ECz+Ta~;z_N_VzZ?lNVP>ad^6B!a zcu#w3qZng#J7LOqO5o^i@;S$K>f`#7={E#7O!Q9g`|DEJrDSwedta=o8+m1FQDnJ| zrfp{Ja;7zTl|>|YeV129>oglFE!js^fA>_jOQl9iYAnj&DAKAXshYN_n9?ts$v{~a zn=z@Dqtn#T;g}chR8IB=VBe-P1DIr(C{J)p(RJ@5eZHDrDcCWKtdqR-?ghSi|1z%d z(*ZL|VuKvCWow3NWTMkrjcqZKAi2bxzJJ$HZ8uR@ZQEtlvWMFM5Uv%Q;q~%7T z;+S(QtDk1_8Xjq`QfC1+Ofw5s_5j}+-cMgCf|qdg8hXzl?zVprp&>zUhfcvD2SGgP zRx$UBtm$<0{jExsF) z%ll?9{a-fwRD@of?1n?XWpZdp9HA*Px7HoFxH3umog~|B4e<%y?U9Sc+gnWVspFgy zgv~X)uM+eYc`5zKGdW)K<(-+nLu>gL5M|eyVBVLADQNi?Tq8OX03cRu!cZ_t1}5dG zKY}7xr7;xI#QG$ncEqc{=`zvfqDB$1dfESSA93PdWEpnlW^!X<#{bN#lm(W?mGs;< z>3Zrhq(`}s_Wg-!WSM}W?M7ezCx1LGl@><(5re$OjJeuTM2*(!wHq|Kso6qr{3?lL_ z#h`t1YEjl}>pamLiPXD+d$k-f&i<+rMV8DF$h_lj!QM;s9-Ogb6sVXbcviI2^MmLr z1&Zbs51MQgj!tZvjOiqDlyKNZ$&)wJ#g1r`-?#IH$vS|2o@*JB9A!xsPpg}?{xjd{ zrhV&UXi;ZqdjmTakda+jsR~WFlS_I9*UGWl4x~{EBdKO?OQSW};jJ;zf)hRj@*x7h zyk|D@Az^m~in$bA0mFCWQDpf=_d!$xDMjV|8GCY$TuS^0RJ8xGNJGZDVVI@dvQ$7UdWcw!04UEq^f6N8j94i+> zT%c4aN4}fWV}|FKdvjd)nfQdom=sKBJhdBtY!FUAoYxQbk5JC;R!Vp$1a;kriCbYi zdnEZ1I4p(P?m;CXUrZZvOK`;!TQIMMaVtE-MVPt-@|%wco;_o#7HlWoUU+@L)!K7s zhYY}4uhbK^j>11FVnLeqyCw_aIQC+9=YEIr9AMjFB5*Ede&y?o*|$vUO{DCmjrpv9 zFB6V#eaUL2Ab#Cw$yBz?0|jDBuDjf@q{g+giF+vr!25WhnVMY~XN0VqaG_&IZwu^Z zd1NVeNyH%}e4z3p*^!+)@d^_eW$n7lT{S)oG@?-KB=vY|1l0kVomX3aeP;Qba6Ti0 zi>PxBVC$q`u>NMu5$Zd=!~7>|vT%anwSQNkQ`rBD24Q@G1@p@qf>ibY$4kjV2Y(X7 z4_?;Ymsq7OlrJmq1q0F1VZ)+I84z@#DCbs_?8R7eVLB z$XRDQTKApGd4BmjXZa-_1=eqUYpeD*@+#{l*&N(rP%GYi5cgk8S-CP*^oKbl76%#d zpGah1R^&axLEmRkejbdieuZl#)OWN8bF50z;k}2-^J+ok+M2xMj&C(MBfhMDJtP_8 zIHXnbea8uQlTE<7I)1&1Z~|OtqEZsbu?aULQ=bZUxOufK@yHYcg_#~20-C)M+=?Pk zO$(eAY+6svCK!o5KFzwaFgCV9F&U!(EhO{e5}Cr92gX6rWPfsxDnDgT68w6d13rQ} zF6IIr-tPkit-B3vLa{Kt#$WH1B-@`jzSIcv0veKSmS0m&h3*{vpVApjpj-fogi#@1g!AxM=sUMxzsxF`rst&?C-(&+#_KVf_vHJ=Zu9@kS1(NvV^ z7omcEWu4}>hCh?bdec07OdXI!cSH{`GDEjdrr%@>c&aTFZxPh$MO$3FGx^5g6}R{* zZ9PtYRl*BO-zLQnK)VEP1oy*M!RxAJs_7(5%xM+ToS_)&8#_j3(&~HZ&|+^EMoueh z3vn!hLdxLjaxSv4GFw-~Ls9|Kwza{1?~^NAFW_H7d|}4US>OxZ(D$Ggo1MR8e%bF3g@S^wd9XX#F%}y{K3g<>j#`kTVG^^TRCwRP@G_GP<9iB_s zrPRveS^~Y@MQkIujiJN3>$qvHV#%(*4_fD_AOo~{w4`3l|B5oog_;o$c2;x-DLA4_ z0Ry0Ye_eZkxyi$XAsw1SRw?5Oe(ViRi;5-U11d}~%qq|bF5{N6sy#Q}of0ZtPGZHB z^o|Pz9%AN0B4nr4=&iulWx9$30X#LXBF!r!8M^p`^+gw?uIo;HaOH~&TTr-u0=e8?rOqd=P( z+M{g>##}1bxm)c((>=0P>@JYoLC^H;1;up9ELE!jLI+AI(QE>%7hqZriuX*M&i=L9Xii!AOfmpjt>YT0QM!FMr7+v~YPJ}i`L9`0$s>bH zsGp9MZu2`L#9?tn;h#>Bm?si|YLRC>Cw{7zh#jvT1~^PEplaxDSe&yNM|t`@OO!k` ze5sq2my{0=0q;<;XF7=`s4HSI$_pc$PT(}S9+=#BYU6CWuZlN1%=}TQu;e|j<{Tq> z|4blb&mM(H;N4lZ!+l78;l;b@D=2hbht)z#Qx^pWoF;TMXrHN~g-SL&8!GBcK}aB8 z77aM<$8uC@=U+$<6g4VgKjerFLNZ*N)AOs%Kf<(T*Lx3RVwjHR zmy~r5Q9(*+)hQ18#XlSJMXluFDBkI69;tK%38}<``Ib4+PnGgI=nY2l7^ZvF@&Xjk zBrCKt?Pz7$RL$YZ1e(elI$vz5So39mV9r}uU+ePF@cw~&ZQ(M8T%PO#|2)TIj$G#9 zOvz`c5g@`-cD}pL0RKcbRQn#I({TrTU-sPgip)%T0>h+__LvG-?0%qQEqPmK6bz7d zpWG#1Etge6>jZn%)Hy6KagW1Fo9yPZt8_X@1sSI{9H)|T zJFrQf=ioS7HUB@;uT?eOQtsbxC}>dsBNG~`3ZNP+_0BDtG(S_jXwb1d?Ikym8FmugA5`GKe6NFiDN@xjJW@k}?*!|coK+Z?v7^GhMa-``pz zti!m*$A7hH`6Omdh1#AJwe!@Kxy@Cn+lRw6AC=POQj9Q$lC?68d_N3WrAN4JGpu@g zIR^22`X$a*mAHj!&3UBnss8&-O{_m8rETKrIddF{74nzn6xLW>8IO712?V`jobu^_6rxfguvz(GnCXYTh0&44wLqZ%*gyqDM0OG;+vVUck}`k5H@-;a`n&>Di-(WBul*-ESRkGV zF@5eI{R(C$@D=RmG5f`hzB`te+V&IgG?0gXqhnD^ zCVjQh(zfM*5b@i6NCp?P<4hcEm-)}Q?s)DSq}x$Z0;<#EVWkLAunN$xJxHA3WRqUt zl=~B^jq|O?YG+d^hFn)fOGjAk_w))M=RM*Qg1QN*M62#4n6C9UAodwU0J*>(QwdeY zihYNuDk6|(UOytD`X2Kc0qMV;vyTuBg8nxaBL6oQgBnaAEjTZ{1=No`GFx*A89N?0 zZtSR0?&5G$3M~)*(Q6LHLI$VPt`R$OIHwyzvt38 zJ+1ohWpiWWUAL7r0%GguS_=Migj|IX$cOKC^G_Dn?Np~XvJmL8>&s#UR^VFQ?|`*- z+tb(ieL|3e(n8B3)$3W-8Ca4tZL-{Bb(-uuSxKU!4UR$+yCPDhCOJ!=qr7tKrzrjCsxyw#*)retb{_Qo0Rok;G$$4P#lxj9iSr&ycbj zJpb1PtxDeoE6D|z!n6nd3JQBD0|>_}sk$Z3)tT7KeZ`)q2>iRyOH z38XAvZL9d1)-6uQ{{weGh`&T{og^+*T0{KKa)yF-TC)V^bvZWX?Q|yEt>(CBuCCep z40BIUI;$CZe_KFw2%M5Mbb7^(Pf^g+P^RI;L|bDSdy8rfy2@*&Fck#plJnDg+P+X= zRzu_V0On(XAGKI0Fn>DT3QiU9X}WC=#WfmO(@?${S#1Fk?ZkP5h z48Vt~e=1aBLjVEHkzbbxwEZ7YSFlN7*-YlRDBI%4W^@GL$85Q4X8?0CPkwa^EFt3i z(*t=^qxStn>+|*?5tmLnRVaWey!I`J3Bh3WCBHdw{?{KRU!of<+Oqx zfhtBS&gzwAsJ6@a^;Muj?+TZ~ z{Yfn+-K-!Zu;_$>ZFxo@tCh{`OrlLHt8pr18=;(PT3U#De8>reXFgWXplR$=`!ZV5 ze<0Hj11xBB2W>mol9NI2wKUU*{Dd0fl&pP>!hkG2tENeuY13o~SI@?NT*Hbh^;_i| zTqn>n6WS*OP}ZMUur4)Bs@?86Ug^gTcoi$#xML@YzJ}MBrP;+CVg$-yJ7KA#@O5~- zAFst5SZ38!YGN7)G){tiIn~u}=sHi&e}&XEq4v9OVIhAD{cUPj<z@DOvY;`Ik^O)sjO<;EKq-fo!0jnd$eemn(a%e-I}fTt4W@U74{b9LiPkh z;F0njigJ_~G*VksoiVXibQ#8;d~RlZPY~=G%4sid(%o`q*~Y1}?P?|y=fy^_f4v*G z`tdH@HqVRO1u6-r3=i2d1utcEe_nSY72Q<)pqlsMeL*&6?oghY0e$>6A=|kFrn}bD)O@(|tI=Hlr*-9D~z3?_yoe zM0dDL+f6Mck;(Q?!6yhS-ldyae;AAE1$zI7Dt8i>OndEq5})$pPJCKm^$Xg;&C<_G znS-VBH~oGJ?jlf&u5e4mVaB4!*s59<`?Qn~1@>o?x7mNarmO zc|qA!l;`BsSpu8kaf2a-$zT{p` zrNsd}BGZ30t*GhE3ja?s>=@S5q!;$HayBpVaNJyv5wg0P_IQBLtA=! zwuXH8#w5`R5&4$1``g^mmY`#Koi5nl#rLWhxbG998#L9_%uo@cKNP4tX}h7|$JDz) z`oo8x2xLPO>uAVeZyi$ge^6Stv?N=OP;%Tk@?J|7Z-NkoLYti(Lgg9R658s#hNPG! zlPHuQKX$yuhoAAf;-e#gpUYD|hF>r`(gMRwU+oy+!!OxK6i?*ClL0*79`rZ#x??xF zzbo~S4qD08)~-?T2i_(O-9|mhhm|QoQeIZvRV#|Kbl{)xXFvXkf4>MUcfpW0qRByd zZ`<@TE1znn+FhD?{1n~R+p}pm+t)>1Q`Q&PQS0CFbQS)Ff4Gg$h9O(NOvc->X+#=# zvf2C>o{?~QR^Zf=S*+kVONr(XJ>w1d!iJq2rmY3fW6Y1|_+&!(gvRxKj1+Gg+2Y63 z*<42J$Y%4lY(3nLe_vEgsvRfqz$H?J$1i4yO8($X`9tRfd8Td54$T@bcmYu*i_9_M zFCEWOwBEAhBg)V>nkJh85nw^+D3;QYCV8!)UOr!P+>T9E@DPIm0`i4dc3hEMSQws@r#U1^0HR$6V&e`DFF zPw*kLTVNy3^7G;2V zcoemX&S9KVz|s+%F3{C9f<}Sca2`J*0{0`DNOX_jEP(>n#zt_SV6pXy?gN<9>`-KP zha=4eT(slB*n{DNR4YUie_P-gLl6}TY8Ad;@f^Ymf1(Q7#%PPj<&xq*m|9g#g88_( zXy6$%SQ@w@oY=K%80(vkpuPDBHjZL*qO)ljF9{z(*U}@16>!-h$iFIVL%b+`3n}TA zi$>9#kQxfOyi;@)u(P{>-4_4r9;3&QTbN;8o#a z*!MX~e`fQcoTV3QoH2+6&bSbD&bS!MoH2ycopB}3az@t$0f;e@^oT-UjeG?bO^h=F zf@4$oFg6DFj^Nq~`nATPu6L+os2Rl#3CS78tB>N1@|+cpS}!V=Jc~J^ncsd?U`IIfipbF_NgO+&zrD3%IwswSX@~_))+Y zV^UA6ClY*+d)!Z$bIqYTPwW6f)cKV}thiOHM?~aSV^4}!&w;VWBM-rIh$}7+esy;N ze_y{HYa_J2y;E+~75wHfzH=BqI0k?4M_dji_|sNTxT%h@yf^r`yK@0gM1sHSbe1ia zVv*g!U%PVdg02GyM-Jn+$8fQn4*s5#NAXw5x(oj-;NJxyiYuE(#Vmq9+%zn_1)=a9 z%?07(P!O{Zjfy#mS}|`}1n(P**vGioI4OUyAWm+RWf7^|Fh&i^r)HcK23T*w>`5(E)~;Cv!$C$;1W zfSU+`TPb}PtHYyAiYEw{r-%sb$BaDR(T973m7e=KnD z$a8l(ZTv{SqJq~@^I9*xoy9Y{wo9t@!&Z-s5?`5#bA2M9B) z4G&NY0012p002-+0|XQR2nYxOll^5+e^C%Umjb)}K(V5r_{FMF61E$oVuQp4rNBcC zq_rkKHMhId?b7|q-Q5~u6=lk%9nU9$l}NdktEA(T^;*d|CS~o8-F8B1FAAs;MT0EXFexy5D2LMW zW$0S_-9xfd4buV(+x4BTcH>27f48}{-Kclkt$MSwxBt8@P;UHYw9=8X#{&AM?R%k@ zJ`u=OR$mIt|DE(S^L&SthLXVa<~X;6b0`)tgYyFUjHOlktWC#-KUB4jl9U1s7X^wg zr3WhFdD0_+<;qzlt7oASF5z+kbC~DGqh*ASfcanCpPISE6$WC%9I=!N&=V54iIl7}IimP9XOKP)i30t*d8B*#Q6mvXhZ69g`1550l@d z2$NuFI)4x_s@=G-6G8$B&Ti_q+0wL1+Jc1GgYYOEcmN&>;eznN^8eYt?XT~TPXM@p zset$G_C9@;8R`wWTrQ<9(hUK(Ob(PRH)8ak}HiP@_)vaOb7CTZ!u5j=krwMG|0CJ2m#ZF zruUj|j3oi5jW3hZV{R#V_Sm-Mlhv<$`ct=P+|jij|Bhi-z~LGPOf0%Gxy#n1yBPKb z#PmYC?|5N!eDcU(4`LWYuw?=VV+9fC9f*DaP)i30LH+M{_y7O^ECB!jP)h>@6aWYa z2$NlY5tFECIDcAsd{ouF|NYJ^cXBg8NC+@2GD47SlL#te5HVp5BmoIahef=Zxk*N5 ziL(UaLe*-mt=nsDD{A|!wM}d7W^oct743rB+EriezP#>>-B+vTeb2dfl9^-z`rbc} zPr|+ToZs(ve%tvi=j2PTJ@y0A zNYqG267fJR5jHWNG^3`GGBMd}qynK{Gju4GiKP}dbsN!?S--fiClE9G0uf2$ysni- zc;)$kO|Ht}cW0te45WIEz;b+=@t#QBG?S5d4@UdVWD09xd{x6a4XXlSvw!h59%3fF zGm%M#%zurMsL527NcJ@LB#m&?Y&@Ja`ufad<0kdF$NFkFB5{qJOl6lF{YGQdi1##Z z>$=Fvox8brY2`h-Pe zadnMFBV~p%$w+#jaU#rWFL`O2PNg)R>5NmuYJXJ5Gz|-_gR(4%nHEf1Vtf|F%c(-A znKX-O?o?13&1NbE*|tPT854@h5sjPa#$7wwKxi)cbeco+n7sKj8ZBUQr4ze$v`#{6 z1=<<3NT-G5FGOqAXfaa>*6f6j#30739BRI{y;Ma@by`Aa!7AM_u7|1%tY*P!RLkTx zuYbtE$CxUs+a{WIbHr{#1mQ~Bh1jaGuCbi(q;F}(mpjsSZVT~JErQxmu;;$|9MnDYiT+>ub8w%+XC zn8?J#8w^1O7y}KizBkw}0$z_g9+@Jq`ZA`q+S+T@xGVH=-G{2HWA?SRrhtLdl4& zpYmdE@Lsx0@_8&5wbkm)$)quWh&=V!-e&R?QdRshMtvhUxL5JjDao_D<#w0Y!5G*Jwg0A`if3Z(N~#7AmE{| zGX+j7NOL#Xwd0XS-;^8R_3Hcuot~%vf{cN{zDw5}sPoW^_0efgdl{kH#t0mc2(RS20mV;q4%03xU(;z+rq0q(0@X+)p4w^- zc+q5`e14Dx)0~N-v}7XDFfuQrrQ(2x-8#EuY2%g^RewAT%%b8?L1wj=OIQa9E=BxE zC#*>?PeTcVL9|KJQ5_&G=G5!uGWsGk!!woEp~k)_iaak@DDyIUA9oa;WV%;HgH|uk z<~gtu&xMSMct^sn3%oo}YWOLhkKM26A&c5s@nd{e2`}Ykx!$G_K;s&nYh{4tH6E^?B9KW3=LV^l zMkey`a%ihBGqDP^Bju@U-CQ{3bNF28H0L3GS`y|LoP0jhlIp@%Vv53$W%BdG&-zi=7rJm29k_ zpyp`Q%l6R5u`04bR*?;=isa2Oa9~qLF0B=)v0p^Rj7G+8>(#X z;O&Us1#D`(!)oPH*dJq6@5B;EmK9#!$-7G6iMz4cavR>uZ<4$H0S?M2nA#BQlZ)-c zE`Q@%MoZ#MMXtpDx)j?80|zH%mpo|<34vB*QC@+7vZu$0s<1ZR>M-KOe2Y~-lD9vW ziKZji$bPH9YVdHk&ZZ12i)^TH!c6&POV?}kn|>ocV1WV>oy@W+JIh@#%x2i7Es;2s zfu;^27_Q&2v3Xb9&V!qFG_P;laBx@WhJPIgH*ag-;N=(!SdMbsIw8qveu6yTf8HS@elw%1SzJU4`|x0cIx9d*<99)18MK!b4L1{Y zXo>wEo$uuLVogg5rlQ9j_EPI?Nq-G1yz?=>y9DUyaOM|5T8~~dnlQo|zpuEb7Ne>$ znx5%#GkrLbJhU?sGZQj6Gt$`y`2G^UkI~l50k8d#Vsg-{tDZvEVr>t9h(E0J`x$M| zit1ugTW+$t2yUyTypKxs2g?YNX-?FLb%l+p!h@x%vzcxyN_&FwRu?;dI)4RAr%?Cm zV#XiK0=vEZasGr(F8<^UH=_+(Jicxu-k&&RHnu5A+Re1lZG^zvfW{9aFvP|On4ZfI z3^pDxdJ|zQGo`Amz*8jEO@%0r0seQB){>{jt(iQ#&WJ`kBeLk^l8pJzI7%8hqQot=&sdnIJ^6O4}78;-~>u`6Ts zebXl#;qx>6tPC&ciMi3k&%xoNMk?KEHAi0ls#P?84b#xoH&8L8jDK!(R}xA1j44ji z$4EcVFUUZFW_DUS(cHPNwKZ4mzo-tc`P;|=?d#9;@ON`3rDGQu?Pe-v^qA`-J*F&i zzi(w|Wt6zQ7+F4bhAvJ6{QQuAr1KB>$4stWJ2wVac^Dn42V`3Y(lUz9E=F@-iM7 z-A)hxdjh1DYG1V=UjyWokv@ejNR0`$#uS`zSYv4L=9x!A(SJ-T(ywmYnnNL|u-%A5 zizso{UDA=D+3K%cpA>_#ip zYsBMbG^Mn<&ic^AS-Ja`Ng!?DM-$adB6-*&YIU(xwtsE9RF(zCbY^wljao7KP+mYZ z09Bw9)zZlUNmNFYsqo}Hkd})Tx>zR8VOsrva6?VVc2%AJt&1j7<|XoAJvuPH`LVj1 z$X&yT^TjG%tP~d%^lUqOVYRR(RwELmqNdp=H}@6^zD8W6iwnitT(e$yv7?D*K!)I% zUa^jzm4Dv09$K(3*1cjQZP!JO*d&YNNS8;nqA)Gu!7YhI8k^ndlQ~cwl%eLr#@VWi zHW@WaqKE}jcKB~i;ZBMhF{zcbOceVj+*^tcu}wPY_S`X$eGROfz75$&>Tid5$G^0Cv1}(#+wiv$9na=8F^wpX@757Q{ZK<*r$u2*zYC7db?E0vaj&w zdJ1f7Ghe2QPGKPXARoxhWf^Va>99451w$e%Er-ojnUc5g@T?>00(R$BPraV#5xo*! zCPrAS!9FO68ku;g*Gx88rHizeM;wwC0;U~dmY$~D%*C9Th)X>rJmj(N1g$!c>EhE| zSbtgs@<}GmZh1RlSBjvW6e*obMY`bZun;6NM^1G+dY z(D}MTa<6&C)za@f#WhSD#v`NZG);9|Wp|f3ZThz~@5pO9^E01)p)1~u;A{6$^2*C2 zu9JUFQRG}V?_g5A1zA_zz|`o6Phg?2|9`L%Ndrhlu;J!C?GUMBD8ad*Pi;Qe!``(xI?F% z0XK+v~Eleuy^L zx5>%2M`;Jsr$=aK(D^uN!L5$E&hp*0!?bsZ_MO-&$7_e^vJ-?#g{D)G4$yq6qH0=8 zLfk3;WQm-k_!Jtg(P#;=Mr%g_Xn%b-6OED%Tsei;*+2lq0r74{O)?MH#e56ib@{gn zmS~y}Lh3}$hidC`JcsbxUEW)Md6wcsbVZiZ)=%3A^#}Lw?--&Z&PV8K*W*+d3_8k> zb~?+i?aa~*<#mtH+jFD0VDvUQx+gbs2S(m0M}p;d0!2bl(WwAAf9ej?e?a zz;XIWmOe2=pB|#)Ba{s`xdJ}t5Iy=RonUHm``nMx(@e+sS)WV3f0^k?kZ#hl^tEIB z5uaB64P}a%BlJ9QCF-{ZN1wy^x3l!UW8?#x1_S=cryb1FPqXyvCfDHTLzw@qns1Qv zWoxqZhm{hr5}<#!Kr3C&%YW3{kFxZ4iF6o9|5QkRiR2sy^=a;Lu^MfVBrUv;@m3bFX*ZQfs1gNrqt7+MuAr~vU8Q7r)Al9|7*v6u7668^D-%FrANuy z)4=Kn9G@(*z2Gqffw6R~N7=i4VSJOwE}Mu~wpFd4YUC$LEx6EgGQ*gB?Tc zFTW$pOOA7Omg`_Vmt||(B;RtDc2{s9%V!5yYWEU!gU=ONUb$y*^m%+#YCgB4Qj>zX zotH^7yAN8kk4Vq1tAF5CL%e#Jo10v6$zb51&o#vBv%IN-TeI9|t#FdO`1HAl`I0?8 zXR!Pz#=zH}uY!<1*(5X^zjWz8qN&fil9t zAekd<1}nH{hp0)Lb%fs^Y<~~b9_I(J)-ZqM;1GYT-si4+j7Nw*l@~1QJ1h9{T(m?qQ!$Zmrv;;Q zKWSDBR6qS1-LKJ88hxJV6)khH?Jw;&wCc&%l9HmV~fPS6>8b!b? znTiI>`SqkvHE;b$pgB_jAtYM>XP%1FQ7R?(*fd#_a({S!-mpdwstM41l^P{?|D=Ud zCEPhm+oe8qnKLFKa3|5304&AOt5jo6T+E{s%2zbsAX!y;=OUS3)VoSICuunn4T@a+ zzXVeaU^ zR+=SlrhiKDeVQ$PM{~r#X|7{7`5g0Uo?{Wschu7Y#|5;|v60SjTuO@^Ve&h!q%$2y zX|dxZEphybs+_ZEsdE9H<*cD)&Hz^A$}U}9Be<%Uk-L4?W%1%ER)r~BMZ+8|9E3tn1%5LAZwTUq{2lc$2eH_Sg#8?}NF zMt_;*-;VH02(r$V*lK^O^kB>UwX7=3f46tx5dQ=FPp$4fXzj!%O-3xwaef(u5KL5> z)PH@>rV`XDK8(B~N5maIS5ry7j0locy`*%UN5_cC$StXO=+qk3GFjEGW13gCGHwe>?{dRADqRB)?IBWXLmND--9k`S}T zurVEM&x$#B({d~9Osmg|d5ST=3?ve__J3f7Sdbs1WHjM+?id#SS>nuCg;;W-h%HhWDL{=Sz<=WU z5z!WG9}?~Oz9iUwlFI6zaNb9Hy<sdh|b{tt$^5>6?@tdH5UdEG>653 ztN^=R!=k%3D=x1P(X8mhY$;-D`I^oOaRr7mV-+dm>#99jadf;;ZFAHD?Akgz_Dy=fOWW|jY;wEX{l79kS*VfyL8pHDGu!)s7fcTDa&@q6LDF9T>Tp@0+ z9TM+6fdJn}{f>8tTWNr9QqNoI9{J=K`G?{HB#W2$uj=_Szbc=CMTvTr2(PHYbGn$R zp0mXw^;{xq)U!owav-paP2v&--zj#>r-L1(>N(9(rk>@FD)n6ESSz1)ihuek%^gMM z?a}yz44!-+D)d~qmHFaj(qExjEYnJH7!~kerMQQNRCbz<%rOO=0#PA(HKKPO5RHN0 z#lvnpiA*L%`A`z%X4yt4k_%+U0+lt0^`ca!4|`&k%!hJ96H7I*43nCuapq<(M%0%W z%kaAt#6-&|M#eB|au`d;Fn>JA7i7`0;bqG*f&TdNyJQPw@%4&g_FvQ~^Q(J|hy=F? z&6K^4J(?#$9XT;QHX&`H1SA_sDa$W$Cl1LjOWdl`UOz3Qc}RPUkoKy;@GEB2C497Ec3A?>vy?cIVkh3f9`zjzOxUSb}GZ$8AI=7;_VP)i30OlKHPf*1e* z?J|>r6C42|lhLFWe@Sk0bYX04Brz^yY+-YARa6B40RR910F74*d|PD||9?r_dz)sj zmTt=!qm&K0u4%_$Wds>-V550cZy#S6;?Fu(s zeDTIL@2>B)Vi(w{czvWk)>q$DA9Is~PQvmWHx*90ahvODJ7HTHo0|hxCL9~EV;5wy z$xMBu&q`$MruxDDaMBtKJHlgiZ>tq=J(jfTHO2FN*+ha1nE@+&6j3|X@1$%y?WFp- zy4_A^D2wZBf0~bOUK5Vn+w0$JLMa5g+-y2#Z*UT}!eTew-_oD9;t9KDN7@=3w9_r^ zsf=eO5=)OVP^K_1?z?G1SjQqYZcNB z6ZM`7E2=jW%eSoK@^gZihw4g{qc(^Ds^n`y5W)OcD2Q2@Enf!*F$Z(y>ktKhgPg0u zp#d1Ee^V%<>*>FP8kToVjv=iJmKtGTslu#&+dJEmK<1-0w|KB@dnv-T1k7d26=Ka3!_<>wb0YzgH&80-0()iH=ZqsB8#K2 zN~9f4;# z{S7l_(3@E?!{Ma`*d~RBzH7(Z0yrIKC>;3~4;eU<+U5yQcawC$S(1>QID0~w=(;H5 zf7wX`8|gVa&3j#YK<%@srAJ+DD@hGDVRI$Aa1QTypXDU7Y5Pq2!RlwqR8N&K??6LK10j7MVogDNo z>fi~+qUZ@tDQk4ZyYZd?-i7y)G{F@SPp8dmSiWU)&3GT)FY-RXOEPKCzz2(=f7Gnk zrPG#{Y2ZTvTqZ@tZ^h%2Vp*tQawV_8hlTD+CeTC$4SbZrbUd3eaG8PgCz#M)Sf_Fy z!^f*|6|Sb0Z`?Os%DQ?+YDYf6i9iq**nb6tPyPUxenG>c<=mTc(;2z}Uf8a37>UhA& zpoK%msXJr#VE)eCneRXOQaqZs<8H1sXY}PWaW9dy&4Rt1)uw*>wo|-JL3{`I3zzTG z8%3>7$@cZxX*<5rwsht82J7aVbeY7p#UDl4;0Eb zZ`u%EW8y~&jpKwRJf`hxe~$#PA3v6ocHmfErNaJC0@#b6^1_fyyn{nz5RZ$?_TmYO zjV0U+SAHgQ#a{fpcw@Dg5|96K!p5e7w7Vle3jT^tX>+rQcwNf%>iVQ|)$vXZ)UlE= z=YPXXGexEsQ_a9{8L5obXKzlkkS=MMRO2Q`=^6Y!fZyTSNwY+;e`w4&OFSnx?~e+q z*~Fje4mv60rXp1GFVgpHuh5=?_^Y_**Z3P%b2H5;PB|w2&apvKF6~l(k2Um&w=~R9 z@;~r$fPL_v#hRZlV{#+tzJDwDHg_H9h$VYG`5(MmiC6GniuT+NcL#e9Ulik_OR1+6 z{Xe`Oz=as2Av>H@f85=XF%{nkCdX^fa#Aem2bWsWHejW@>YJKY^qjr(|C_dX5-Q;7*vO`o?U^bCPz6Ehh!k$iWoy5;(rkuX8fgr;aa6Ctk;PqW79jf3=>0YU6X8N_2UA(VuAzZW2v7 z%t)c^%qDy7v|izZt(=n~ZASUrdGcrj2!jR42b+d`u4%~U9RMHcYj6;slDq%v#!)Pbb~FxQ zVGhejf3YIk*fWeKjjqh$nCe#k%i*|ToG^q%Ih?!;t5@XEwhPTXGoQaj(Hu66pd)(b z5Z)f`+=q(Y{y8h|KsT9e$-&AY-rX3DZY4D-7IqF{aiomLBIQF^5{*YwU%PIf~1ok-#u6 zzqhr@-x{n9)>eHUhlb4B;Hqe3mR7nd6bSL_Bi)w<)$XyULxG4HGVjDS3i*#uD(u41 z^0iB`Z7(A~>VLC1BoyeW{_HSrp_zGKW7E%=rA73;mL@Z!!JT+#Mq5aaad(Y7Vc|`7A-P*s-LDs zBltrOf2w}|fLXbwpM;d9baqS@OpPK1^8R6ncZHJ z2&zi9qmeQRaP>$O?d!qgt z73?ajQM0?sTPt#EUTsBB*RVP$rxr48a%#ygWW*7j;)aM3;!=I}!#(ubqalNie;8Fu zNjI#P(Vb6{U>_Pn6*cO}h*@?IjA*3NA2Pb=?#i56!C*esxf^r&TO^ED@?(B@M78D= zjen7t85S7chr>c;MK_iA)TrYpWkyruikw>8tuIiV;O(8^+eg*OQMnDnYTbSEosVse zYSU-`RHIHU1eg0*g=_d;cn9vnf6bh{1>VMSTHp{zRDs{cehnYO!y5jA1Cc-(VFdn> zLx#Xt*_H{}a0437VjmMIokn22I!?nA)kY1IYEV6mr__b&3JtGRS7~^)x>3WM)QE<6 zt4B3_R6VAi1=JJj=Nf-jJulFAmG650Y}KM+K!trb`97y{fr8)S`;x{5e+qu9Z;!?W z3O?c+)wn>x@AciUae;zA;M=Ehfr3Bi`<2E83jVb3IgJYx`~}}j8W$+|%f44ME>Q6Q z`YSXpkhs6vzd&#eiNmK(W7)kNb^pUT29_DaaM8!Sicc`!mw;8JCHTX#-&K#%VR)Go<*wT%b@r|<5e+_YYe&ZD!HpUKJ z#y(vjrkcE zBdGc@OC>Sew-$4Jn=sdR9_IOCsP^@v#&<5=K#u+X1C$Ums%`1RP~ z|36Sm2MA9re$T`V1ONcC7?bg3Gn2S{F@HVXc()3kx-R0WsCXlYf+8pgUZ%U#Z8Uoz z+13lu2k|Yu5Wx!{z=slNt0E!;nVCP|{0YhX$Lkw_4a^8UK0KT^?%bvfZYT-e9XDvX zbvH=kOlg^`H1XmzB-RaSl9qV0Ev*-{DY&tn*t$C{sV&vrEb?NRd8+W(Y;MVLYk!+r z)A*Thb+l%|wxzemEhUjkh>S`iR=Z>@pT&A(b$zwrh17NLhadzh7iq@?bf`25ETks# zBO^mi{;iQ&M#eu*Y%aB)|IP=+#meXx7{8WX>1&xp{#o;yg1n4D_WK$?N@MmLJLxeh z^$Y)97Fts2j-gYsRz^%rocy|6d%;Z0(xkvXHohDP)i30lnTR?YLiWVPJg9X3w&GEdH+uIxRR_qY{yAN0=cncVoR2t zgvJgEFUJYsSb1RQfk;ZYmagqfBwe9<700{=YuGy2*3q)HNmpQW%xq;{vw<9%LSXBF zveB-4cVl!L?H(;%JGO3v4ZQz%?v*V&GIU*j`RUy6obP<+JKy*J9>=e|_r>Rk=zl}v zPC=*dzI$-%9nHg9`k0>2G$)$VBh4MnX){+avYKs}`FPIE=$J3+SzWVqERJbbJUynT zk6ERh)tng7vXtwHSA}%V51oKatLsEaSM;t2dq2Eo--y*W@WzR&O@)wqDF@*{%^Vc7J8f^f6qx zYv+R7A>4n3kvHtC1bw*eee``_4Qnm#)9kTc%hGehS!{1VD9F>+elSc+XjzC9su#5F z|Dm@+jUif2^3Q-+@T?BV(a@YEe8#f9Xt$9J$q1%$unTFZL zhq;t=?U2o=+1CC(o7cNzAAiG?eLJe#eOb-21U0s`SILr-+ro4Stz|2yg2L6uD%1>z z=qC)zwxq#s3e$RO4N(hSItOl!P71XNYLc@h+sJnHnb|B*2xMCdMFj=*T*015LYkn4 ziXM`a=b%Oh#X}UMPOxS%!z$q1`nLANbFC4kjkJli*eq!2yfp=ZO@EEEqI-))O`fSx zcZhn}({+Zm!ze;Cvp5l^%bg1)a6v5t^f$F7=f}}DzW5b%CGQ6^m&{dMp=$&whP9J# z7pCphT1UOqC+L>zq<7Q|n2N@5i7laSXtg$|8B@2^ylJaxGjD4~Ue)pwU~_abbgNU{ zd7=P9Pkju`ojs(+u*(sp)2-892D(HWqf@Xv@@%xN&`*)Fr zwNt;K4L>5R6dDlJ()NKcl`*zEL`m8s$ZHw5>k>)*VcJJGu%QMK>I)jmwT}fem}>6F zwbFhZi4b7l_P1YXkuV*kL#)b;;L94r0lJA10e#zR7-PF>+J8_}E9{11L$+2#s#w2C zp$~`XW=2>0T$|*z9Onz0vrY{d-@+$pf_8l{R`__W$XA^~jap+D?wc000yV`LnW*H% zKDS^A+EN20AM8W`eCYb#_~tF$0UAXqkt~*;E)@-XqH8yD8q(knV^rsGFc4xew?s=m z4S#Q{ai;5s+J7=&nq!m=(X9lHS5|A+pD&bbh|sm1LMA7Nxyn0uyDdZoLNQu&c)LP& zB_Dui&i3N~B)$;yzP7{L8ImVxB1GeKJEE#o$Y?fnSFqII&tmVSyI7;UE8^sB_Ky|K zac!7$PC^#j9;W-~r+-+;Pgky0Ws>bBBb(t`@-rd2 zpOI8Q%h8X5B&j7+reh*!dM1xn z8ry`-J+1lPv<-(;O{?z0LBld^b0(UR1VV@=u8N`;aTL4QvPTk1c~vY5{T@OMubtgyQQw)>bC8P2{C#e3zDzG759Rd} zw!1Jtwr48q%k&jye+3ok0(*_n=UQ>8l*cuhQ3$aTe^yIp+5lHGh6J zX-+f3nepprUM+1zW(1Zc=+Yl4XF;<9xnt-aaM!js6bZr7WE@tAe`PlC@1& zxy;z9#ZeT{j+P3)GS&;Vx3rzoQfA$ZwXZa+1aT_v;A|$C=1C!)QC&P1~wO-oejWh zx|BuBcEHk$y`zvA7EvGs%P}B?XXA1@AmWu|bb(MsbU~D*+k;~@NbDDfu)(mndoC7B1#~!JkwQ|( z%1u7vf6It)68eTw1c=4Yc|Cu@pRwjg_4*z9h*rwl6?)&i?SDA`W^t6=e9PRwEB#*u zDPkDqxzhaMv1ymAzA;=>myecRyBI7Pp@&3Tj3Bqpw0Gm0r5dxh?hJ@As6&W(3W#G! zt3~-R-EW3PjysSRfyk?`PHnQY3Kd4&t7B!lEH&^F`6s7;5Pv;KJ*ngrZGG-4Pq(+pd+}p* zakR<1IhF90Y1=6Z#Ul8)`p`+Qn4EqiHV}P=b_hB}s`pt^QUjijp@wUtXKB~KIZCFI zB05ETC+U;m0<^u4RI?qpfUOYqJVU8P^gOj-z9p4PMjH-K(Ge(nirQlG{B^N&bTcb> z6!dT^`F|oUjXmdml!7tO=1KC3m#UA*TyVrrPI{Qbc;h@gRi$~?KFI| z2s24|WxDT^q5Oy5i`WVDMjM+)>y?+5sRFqBM#uubRx|i-= zx}}??JER0bO1fE)6eL$Vqy&`i5)_v15)f%bx};XhZ{>Tx=Xv>d&wcjn%x|ulxoh{H zGxNW&Q#2W?jQn#8PQNCk4+NH2?{jTl<`l_sti^+q4L-l=BvyelJoWiT{lWDQYz99O zE(yNlzwKdK1iy3m#TyWDdlw+N&OJTIA546G)PKhw@MZeI=~wT&z&GL>;LhP)$E(Bo zGb@g;QM|5IZ-S$`c)P0^`k4M1NGV?K84`izk(F3t@x|MnT0L#{G>+*FneRlZksT@G zDaQeY^2_r{)wTn)v@hWCF>~&dP*X22Xi}*aVpkJ8+X_U9BucCD-gz`o|{g zKg_rqpSAMe^7L=31B+NsASdX^Yv(Us;L)PJ?HN8(e!eqqmvr09ZaQ@Fr(7O7ZJAvR zqdxIGqx9^|v%v>XiZFJvUtROx%6FR`<)T+=O;2GpbV`Wb=K2lwPG`fV#e&CWh*>-Q zJBu_{Bw*58$)j>DLq3iTn;zz>AA#8)3=&*sxci59IK&Q%Ej%=)AWy{}PZ5aG6cm9( zlcQ=@-QQ@4rEMdL{W|i&%+ea14AV|?uZfU99iFNjuhZi7^!QRQK>ll#i}Q>_nJ*)Y zLQd5Jq`HF2mv`v&!|qq0y&6n~1)4Wu{cXS^iUlP>dLe&0kH_nTaG zWzOU|?6SwcB=1WQzJXj5-{P-@1DxPmH(o6(ZX4+Ch9#Hgvv8fWnGsG&0rf3+awNi# zACJ)`2KeSU@^>SR;5i!|c&f@te(j7|DJH&jhNQAve$T?{Fl4QYFeGf4b!nu3WpR_hqU->4IhaK zy6tZo?Eva6Rv9{}2dE_#;geNkl;JhDC2badMG08N{NpF zf=KKHFR~*(Lga$vm-T+4Sx?kCmL(W!u-Rnlq4kl}CKOR(<|_(28n0>7ma0`~ZIlOe zEddBrTbTru;>-cdvnhF9A8v;*G=>2Q%e-_7|u>H z_$ppV#I`S~IvfT|I!JshrJw6~yddTL=5&tEkuiNKaBG0UaGdw~2gFpVyj34*Tos52 zozW7;sk92sTRvV9o&CqZZg~F=o+L1PRpJr=0AqCb5u(Pkg&;^na_nzCh`89QVqL6F zv@39hSfU~#il5rxDs_UJVLsMw`>3{WZtz1QDJx&oB39rsoQI#_aLYBM=I+~HGkH;)B;_zj}Y2H%Qlqc?J#tOx|qG+W3mP6IO$YhsrJN zS4M$ry&qjm^bxYxzWBH|Wj8wu7Cf3lzAU`~%b zETk$$EUVJ0!pps4!sF=eUe-wsuvIba5C!#=AY3ddctPF*tKMeY)yRj}3=^Drj_H|Dv~?)cO(^A>P5sau_v@TB6A%^J0Ol2p7O9NB#{jp$YU$@<`qy$5s zt!+MkV=ryJFZBc3CCakEhkcOYwZ_<&kbDz5Y#9eOPYuHv<{qXcr?;n>TNp9T9&E@c zAobi(T%&$1RXU?pTrl16j*VSCI@=|h;mQH9!5wYA1Cp3iIOI({Qpoi`%k%ruZj|g| zv-8K+rk5===iiJ&;EAUe&#LE-Qq8Pm@ zObP~T*_A-^Xxmub>3`BKcc-p)Kk{R%Rr0bHn%M}x{g`!k6YvKRYCBJGT=#SZU5j6# zF`p}i=!3lnn?WAwg4Ku95%trU3^V?S1USF!)`y6hZf-qRRsq3;sJfUAVr(rVh=gW0 zpVEfj*utt?NRwbxnEHgoGj&8rJ%;l7jGfqujphvWq9UDD#fFq|7kp%Kyx&tCZL?7* z`&+^nwsFbyeN>>PcXr=egvjE~eAv&`~@EFT@W$pG_-%lwbd(W-t^TNmIb*~@bg z?J(T3;QMvcjIkd^84;6bUO;t1sG$=1IuN-E7srUF&dFH9GR(#r9jk*sm?$zv-gt)9 z%~V}<(M~?uwza$_UZ^v?Ud=wbLxY6#_60|&Blo;FapL#9*!-S;dT@Ka!fT1t65}1k zibum`9}+{-(!?@ietPh3mcI?Y=TLs?{)$$~6Fy;dcU zq8*f`O41v!Uy4s2o>d>DOw{Zp59;AA&Eb*wYI-H}L8sC_aT|A0GPaeDqTN z90gu^H~FsvdR>lKfr=vDN2d4}t|-qz`GvI4DXy{wa)ew~hW!&(4N%<%9ikyvIOZ#c zd@-Il)KR^0IL|4Sz;|H>5;21yd7OShn1>?D(cqVGa+ZCiDuKmHZFa164y9+hxlT3$ zte;?;DKSITin^~#$lD&55f^_jXk!H)nmmT>JnF1(_dpg+#Dl>BWaI$}yCmi|+A)=< z>z!m>c3zQu2{;B$DWRsH5Mx{l1l>7biW}PHDEvshL&*c?eU#Oj=3ZJvzDlI%K73Nz zC)eU18Z7Y>>j}MhJB_cTugN7x4-~GWF<5K{*Y6dyCtrSf`>H)#&N8UU?(w^|riL-y zYTPUiOpTF@cqLmRONyk{7wxZv) zxvok|jTE7_)^6rm0shl-@r8@jf|&Bt3ASRB@v)#H4`CJR#>*{`X(2%yrQD9?At<9V z77HoYRq>D=3ex*C`R5VDMEk@E#H3(wM*t(_X0S6O{!F!OSg;nza7}z*Nm-Ml%$e8L z#^kasj+h|7b^AhAG%Vs`lh3B^hTs6dFuLnKj9gsx(M?v_S`QK1_~fNC)$Q*fA8a>Q zTadI!)(C48e&yN{3O%H0L&fZskU5B~_W{InHm$Z%WrQjjy2Vmb>S% zo$WB8kxf?d7x0_(E85p#2|6huvayuEhPC!SGv{q&KmS&W!Hju(F5GY;z>?tj2YC{GGHgIf@&&h zYpQCR9E(`Dz_I%^t58>cvQgiFmoG;oW2kHpWDF^=|7dL8D^UMb=zS``0a%#v>ks|Z zSzWC0TK$0uU3-c~rP8VKUerq8=GT&0tbe*g^hx$9J218q=t6Ucm@5+h+@1n-%(r}CjEywT@gO7td z9o_5@PScQWX1ce;)BJDca`!MIe9SoFO?GY<$2fEkPH-hbVFQPsHT>g_TJ$cXOi7So z2YSD;axI5`>=_{6U8;?a^+=gfb(4&HZ|qm<+662z$oOEq5iI-owyd{lf$)HeLn#sC ztcpF$Mv8udS__B)LV?L1!@!F}af?^8hZlp8kPr#qUmizaeE>?R7~Sztw!`?4Z(TpY z??wQNq%t+(zNi@AmZgx;oV5t)a2PHRvGK!X52ffpR{TzTx;s7a&4m*%YJTD#6a{T_ zIG`OMe-dHDhYsdFd3p0u*!`tYqs8PH@M7N~`ohf&WxIL2J(S^;lHjTIkHp0b)X@t_ z77h0Sw4Yd+Oa~6L5 zqGPw{B}*~EiL2b(S|wg8ib~+X|6C#wyhcxzD>p| ziS5WMxx0=hb>dF;?zFGpB50Y&((8oTEoib=AP*i9#~Zjo#FKa4!)kGpEb?S$owH^) zOe@%@+vymNMbmrO0w?m@4eK|*p{NL4)3iN#y55Z4_P-Vr&5B5RM+l2bWNkF4b(ucI zm&kzl?kUtqE=ESKoU4#8w!v|#W)`bO2DP%`Jt3(caSjXb&cvWbPG$oaG6zpt%TZYi z_F+D#l5Slr4~BQAe8;dX0u@xv4iIH!bvq2aY#;VrX+OHXJ?fPPRP?>WVNC>noAqYn zXDU^0Nqb!pUtFJT%v8CB9m`=BTh$9W4Tzfl)MdbvokJRJCy+<;bBCZlLxj<(zV5{@ zatxY*|0^wO4@1e}Gc9pqpj*WUngZG%8#tGGQ$xdR58;jZvz|jN`7?Y*o%GcVD zMVxtSMai%yAT)?BFQsFriH?}Of{4fK9Qx<_dE^2=@u3Q zXi|wjR%{ZQMPHmWFc-YmAi>EM4H;i1p@$)yjuU(gCC)_HosUIAOb@Op6+_ziwk4GZXf@0Ipq?AzZQr)?td`+XF*^CQ`VH`(A0g8aZ5Y-OO@0Vn-8}mw% zTKq3duS)DI>@v6=JSe1PBpWpG-23vKGA$DWQBE7qe`Zfx@HfoxaTB)CQYo-el`?_o z%wSI>K~hMFqV^(T=%tWA&$&SJazx;p7v+Q2{+n52&-jkyd10^EOIVE_ZTlX$*|l7^ zu~FoNL6uQhzYnB}6_p>jmPu?E1NE^~U+^qeF7xa%6R+j#zKl!ru%#?^tbcLENA|~$ zWSQCRh-rCK!RJIC>gOxIvHn3pcIt3zHpBI=<*PZb=@`KQCLJW&8Zj>7XBODg6YwTx z#32!Vbce1$;r-WqZ2d560+VI-ay6wU_**@~Q2H^5fJPV3ZG^(TO4$Ef z;v?jcq$audDA^aK{#^&UTFIG1-A#q|IvECg%H%dn0Xm}*LQ7b2kBNC2J6^t5j;@c& z!{ar3!LxU~wvz>!Ju)=wuAiB&YfDbAyvnxsJ(}4oyps)hJbvwt!wXXQ1KD$-6+Ywh zkGYEd{w49+otT$zQGdxZph0eufh+x#@ac!MBoB3ug6b=G~QFt{S6^p@eR)OAB<~64D z^$FvcZlv-aIsU*Q-#IlEto3e4jSFx#UD}Oa5a0&!tin0W43#$ZR+Keh-PMaleX(lSWU* zSB{B>_Cs>r@sx6OdS#zWMr@4132)<)WhLL4O}vcnLBotElrqeSfc>#TswHPXRgCKB z;jURF)GWSQu$^@OL{ooK6%XBVko1o{vxi;;O}WRTbyX#A6O6pW7nUwz50FU3IaDH| zbl88B*WW$CPW8@Gk&aTl_fyZfNirul*YXq_p(f-!K&~`p*@6GePQp$qp}HEsuFHJ` z$teu~R#5hdBER62UcnDG^VmgYR8MCK0xSih_``&P@{-~u*#(9QZ7=ujvoy>g+9ggI zBGF42lcrzR!9L5JUhU%xqwWQ}{Ug2F_y`w|n#8v?s3}yApOhyiHJyuR1F8!h3of1a zIVeSKpQq<2FlMW%)4HHUr_Spenyz9#Wek5>THLQt_$SGDEC5q+2xv&T`upCj-~?pT zT7U)sC_t;K-=nTxX(%v2jcaUCtt%oA5};pdNRYGep#*h6=xGfza8{jO%?$Bd*MyjR zBmlttyV&ACuSg;U#0WL2sF!Ugj+5VfTvYI`WOPJb^%&2TS-su<BS9`;y3a_gM}yMW z^8(L}pZ_XEJk|yyR74-tu*doz5Cr_kl^)UNhn>1%|3^)ngXC|Uf%pFB2*ojkBL4%$ z7_NKxGZ*DO_>XfnqVduXz+9etaGHnp9{k7F72y)X@&JtDLx2nj$7{U%-Sw}t;{ON7 zprMU&zs%nE|TU%t7_n2x9@E){jto@&HnT#x|P*?>!k1`80@p z-Us=q8qk7P5-3TF5X#@!@=(ndQsh|8`?RO67|`$*Y2d%XwE+OZ2Zh19|A5ym{J{T? z1W5k?>@@ff$O`-?a2%p3+z%joYXqbM{O_o04?A3BivXygbZvZ8|1MJk05~3~Jc8!8 z0uc;0bi(oea035#65roBd;kE$1NLX|-)3R{v%Cq)S5FJPpM8edS6hhfVFMq<-S?s; zi1Gf&{5#SL0MI?qoqTf-Dz~!$??=cGT{T6VowN@ip}c!2ubmnA&!7;Z%7&-{BR zH4k`S<^3}tLdhO+G4ni7voE{{b@T5pRN(*pSJp<{a2|Hzwgg)6Nd@@N-3S)|V0)mX e{Sg5G5zENPNVU~b5#<2@M#Os+0czL&{q{eZe&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,7 +65,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line @@ -73,21 +73,10 @@ goto fail @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% From 9836290b834c97f51e0c3a73e0480b4d85f268bf Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 14 May 2026 16:10:21 +0100 Subject: [PATCH 02/12] Bump version -> `2.0.0-SNAPSHOT.241` --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index 0ea4c4a6b..e0a62b4cb 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -27,4 +27,4 @@ /** * The version of this library for publishing. */ -val versionToPublish by extra("2.0.0-SNAPSHOT.240") +val versionToPublish by extra("2.0.0-SNAPSHOT.241") From 0654b6e5f230670f1b899bf2b8579b0270ea3386 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 14 May 2026 16:11:48 +0100 Subject: [PATCH 03/12] Bump Validation -> `2.0.0-SNAPSHOT.420` --- .../src/main/kotlin/io/spine/dependency/local/Validation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 600deeda7..d9379b785 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.415" + const val version = "2.0.0-SNAPSHOT.420" /** * The last version of Validation compatible with ProtoData. From 943ffe8fcc0848b4c98f0e2825af28b3b82150be Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 14 May 2026 16:13:07 +0100 Subject: [PATCH 04/12] Migrate to new `ErrorPlaceholder` --- .../kotlin/io/spine/time/validation/LocalDateValidator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/time/src/main/kotlin/io/spine/time/validation/LocalDateValidator.kt b/time/src/main/kotlin/io/spine/time/validation/LocalDateValidator.kt index 5a3fb83c1..24ae7938c 100644 --- a/time/src/main/kotlin/io/spine/time/validation/LocalDateValidator.kt +++ b/time/src/main/kotlin/io/spine/time/validation/LocalDateValidator.kt @@ -33,8 +33,8 @@ import io.spine.time.Month import io.spine.validation.DetectedViolation import io.spine.validation.FieldViolation import io.spine.validation.MessageValidator -import io.spine.validation.RuntimeErrorPlaceholder.FIELD_PATH -import io.spine.validation.RuntimeErrorPlaceholder.RANGE_VALUE +import io.spine.validation.ErrorPlaceholder.FIELD_PATH +import io.spine.validation.ErrorPlaceholder.RANGE_VALUE import io.spine.validation.templateString import java.time.Year import java.time.YearMonth From 9987c6f466084bf98c2b9a4034271de95ead86cc Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 14 May 2026 17:10:18 +0100 Subject: [PATCH 05/12] Bump Validation -> `2.0.0-SNAPSHOT.421` --- .../src/main/kotlin/io/spine/dependency/local/Validation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d9379b785..c3e302c79 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.420" + const val version = "2.0.0-SNAPSHOT.421" /** * The last version of Validation compatible with ProtoData. From 3b6073801b1ce4492c9a37163b4a52cc6bfe973c Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 14 May 2026 20:53:53 +0100 Subject: [PATCH 06/12] Bump CoreJvm Compiler and Time --- .../main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt | 2 +- buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt | 2 +- 2 files 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 0a52ff782..6ee3b088e 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -46,7 +46,7 @@ object CoreJvmCompiler { /** * The version used to in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.063" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.064" /** * The version to be used for integration tests. 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 4e285fa9d..e6c184c9c 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.238" + override val version = "2.0.0-SNAPSHOT.240" private const val infix = "spine-time" fun lib(version: String): String = "$group:$infix:$version" From 7f285bd53b1de4e22bf73b73087b7b9c329d893b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 14 May 2026 20:54:14 +0100 Subject: [PATCH 07/12] Migrate to new `ErrorPlaceholder` --- .../tools/time/validation/java/WhenGenerator.kt | 12 ++++++------ .../spine/tools/time/validation/java/WhenOption.kt | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt b/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt index da910ad11..f9916d2e4 100644 --- a/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt +++ b/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenGenerator.kt @@ -43,12 +43,6 @@ import io.spine.tools.compiler.jvm.field import io.spine.tools.time.validation.TimeFieldType.TFT_TEMPORAL import io.spine.tools.time.validation.TimeFieldType.TFT_TIMESTAMP import io.spine.tools.time.validation.WhenField -import io.spine.tools.validation.ErrorPlaceholder -import io.spine.tools.validation.ErrorPlaceholder.FIELD_PATH -import io.spine.tools.validation.ErrorPlaceholder.FIELD_TYPE -import io.spine.tools.validation.ErrorPlaceholder.FIELD_VALUE -import io.spine.tools.validation.ErrorPlaceholder.PARENT_TYPE -import io.spine.tools.validation.ErrorPlaceholder.WHEN_IN import io.spine.tools.validation.java.expression.EmptyFieldCheck import io.spine.tools.validation.java.expression.JsonExtensionsClass import io.spine.tools.validation.java.expression.SpineTime @@ -66,6 +60,12 @@ import io.spine.tools.validation.java.generate.ValidateScope.parentName import io.spine.tools.validation.java.generate.ValidateScope.parentPath import io.spine.tools.validation.java.generate.ValidateScope.violations import io.spine.validation.ConstraintViolation +import io.spine.validation.ErrorPlaceholder +import io.spine.validation.ErrorPlaceholder.FIELD_PATH +import io.spine.validation.ErrorPlaceholder.FIELD_TYPE +import io.spine.validation.ErrorPlaceholder.FIELD_VALUE +import io.spine.validation.ErrorPlaceholder.PARENT_TYPE +import io.spine.validation.ErrorPlaceholder.WHEN_IN /** * The generator for the `(when)` option. diff --git a/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenOption.kt b/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenOption.kt index eed6573a5..48c93eff5 100644 --- a/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenOption.kt +++ b/validation/src/main/kotlin/io/spine/tools/time/validation/java/WhenOption.kt @@ -65,16 +65,16 @@ import io.spine.tools.time.validation.TimeFieldType.TFT_UNKNOWN import io.spine.tools.time.validation.WhenField import io.spine.tools.time.validation.event.WhenFieldDiscovered import io.spine.tools.time.validation.event.whenFieldDiscovered -import io.spine.tools.validation.ErrorPlaceholder.FIELD_PATH -import io.spine.tools.validation.ErrorPlaceholder.FIELD_TYPE -import io.spine.tools.validation.ErrorPlaceholder.FIELD_VALUE -import io.spine.tools.validation.ErrorPlaceholder.PARENT_TYPE -import io.spine.tools.validation.ErrorPlaceholder.WHEN_IN import io.spine.tools.validation.OPTION_NAME import io.spine.tools.validation.checkPlaceholders import io.spine.tools.validation.defaultMessage import io.spine.tools.validation.java.ValidationOption import io.spine.tools.validation.java.generate.OptionGenerator +import io.spine.validation.ErrorPlaceholder.FIELD_PATH +import io.spine.validation.ErrorPlaceholder.FIELD_TYPE +import io.spine.validation.ErrorPlaceholder.FIELD_VALUE +import io.spine.validation.ErrorPlaceholder.PARENT_TYPE +import io.spine.validation.ErrorPlaceholder.WHEN_IN /** * Extends the Java validation with code generation for the `(when)` option. From cdce2f3352ed9d9b1542d09eead0acf7b3a42b1b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 19 May 2026 19:09:26 +0100 Subject: [PATCH 08/12] Update `config` --- .agents/_TOC.md | 9 +- .agents/coding-guidelines.md | 2 +- .agents/memory/MEMORY.md | 16 + .agents/memory/README.md | 89 ++++++ .agents/memory/feedback/.gitkeep | 0 .agents/memory/project/.gitkeep | 0 .agents/memory/reference/.gitkeep | 0 .agents/project-structure-expectations.md | 2 +- .agents/skills/dependency-audit/SKILL.md | 115 ++++++++ .agents/skills/dependency-update/SKILL.md | 278 ++++++++++++++++++ .../dependency-update/agents/openai.yaml | 4 + .agents/skills/kotlin-review/SKILL.md | 62 ++++ .agents/skills/pre-pr/SKILL.md | 173 +++++++++++ .agents/skills/review-docs/SKILL.md | 129 ++++++++ .agents/tasks/README.md | 128 ++++++++ .agents/version-policy.md | 20 +- .claude/agents/dependency-audit.md | 15 + .claude/agents/kotlin-review.md | 17 ++ .claude/agents/review-docs.md | 18 ++ .claude/commands/bump-gradle.md | 13 + .claude/commands/bump-version.md | 13 + .claude/commands/dependency-update.md | 16 + .claude/commands/java-to-kotlin.md | 13 + .claude/commands/move-files.md | 12 + .claude/commands/pre-pr.md | 32 ++ .claude/commands/review-docs.md | 21 ++ .claude/commands/run-build.md | 12 + .claude/commands/update-copyright.md | 12 + .claude/commands/write-docs.md | 14 + .claude/scripts/pre-pr-gate.sh | 73 +++++ .claude/scripts/protect-version-file.sh | 37 +++ .claude/scripts/sanitize-source-code.kt | 29 ++ .claude/settings.json | 88 ++++++ CLAUDE.md | 70 ++++- buildSrc/build.gradle.kts | 2 +- .../spine/dependency/local/CoreJvmCompiler.kt | 4 +- .../kotlin/io/spine/dependency/local/Time.kt | 2 +- .../io/spine/dependency/local/Validation.kt | 2 +- config | 2 +- 39 files changed, 1514 insertions(+), 30 deletions(-) create mode 100644 .agents/memory/MEMORY.md create mode 100644 .agents/memory/README.md create mode 100644 .agents/memory/feedback/.gitkeep create mode 100644 .agents/memory/project/.gitkeep create mode 100644 .agents/memory/reference/.gitkeep create mode 100644 .agents/skills/dependency-audit/SKILL.md create mode 100644 .agents/skills/dependency-update/SKILL.md create mode 100644 .agents/skills/dependency-update/agents/openai.yaml create mode 100644 .agents/skills/kotlin-review/SKILL.md create mode 100644 .agents/skills/pre-pr/SKILL.md create mode 100644 .agents/skills/review-docs/SKILL.md create mode 100644 .agents/tasks/README.md create mode 100644 .claude/agents/dependency-audit.md create mode 100644 .claude/agents/kotlin-review.md create mode 100644 .claude/agents/review-docs.md create mode 100644 .claude/commands/bump-gradle.md create mode 100644 .claude/commands/bump-version.md create mode 100644 .claude/commands/dependency-update.md create mode 100644 .claude/commands/java-to-kotlin.md create mode 100644 .claude/commands/move-files.md create mode 100644 .claude/commands/pre-pr.md create mode 100644 .claude/commands/review-docs.md create mode 100644 .claude/commands/run-build.md create mode 100644 .claude/commands/update-copyright.md create mode 100644 .claude/commands/write-docs.md create mode 100755 .claude/scripts/pre-pr-gate.sh create mode 100755 .claude/scripts/protect-version-file.sh create mode 100755 .claude/scripts/sanitize-source-code.kt create mode 100644 .claude/settings.json diff --git a/.agents/_TOC.md b/.agents/_TOC.md index 83375f417..dc8cde3ce 100644 --- a/.agents/_TOC.md +++ b/.agents/_TOC.md @@ -13,4 +13,11 @@ 11. [Advanced safety rules](advanced-safety-rules.md) 12. [Refactoring guidelines](refactoring-guidelines.md) 13. [Common tasks](common-tasks.md) -14. [Java to Kotlin conversion](skills/java-to-kotlin/SKILL.md) +14. [Team memory](memory/MEMORY.md) +15. [Task plans](tasks/README.md) +16. [Java to Kotlin conversion](skills/java-to-kotlin/SKILL.md) +17. [Dependency update](skills/dependency-update/SKILL.md) +18. [Documentation review](skills/review-docs/SKILL.md) +19. [Pre-PR checklist](skills/pre-pr/SKILL.md) +20. [Kotlin code review](skills/kotlin-review/SKILL.md) +21. [Dependency audit](skills/dependency-audit/SKILL.md) diff --git a/.agents/coding-guidelines.md b/.agents/coding-guidelines.md index 3297d8ae4..12ede97cd 100644 --- a/.agents/coding-guidelines.md +++ b/.agents/coding-guidelines.md @@ -33,7 +33,7 @@ - Reflection unless specifically requested ## Text formatting - - ✅ Remove double empty lines in the code. + - ✅ Replace double empty lines with a single empty line in the code. - ✅ Remove trailing space characters in the code. [spine-docs]: https://github.com/SpineEventEngine/documentation/wiki diff --git a/.agents/memory/MEMORY.md b/.agents/memory/MEMORY.md new file mode 100644 index 000000000..cfc2e843f --- /dev/null +++ b/.agents/memory/MEMORY.md @@ -0,0 +1,16 @@ +# Team memory index + +One line per memory. Scan at the start of every session. +See [README.md](README.md) for the format and routing rules. + +## Feedback (validated patterns & corrections) + +*(no entries yet)* + +## Project (durable context & rationale) + +*(no entries yet)* + +## Reference (external systems) + +*(no entries yet)* diff --git a/.agents/memory/README.md b/.agents/memory/README.md new file mode 100644 index 000000000..899d9e558 --- /dev/null +++ b/.agents/memory/README.md @@ -0,0 +1,89 @@ +# Team memory — `.agents/memory/` + +Validated patterns, durable project context, and pointers to external +systems. Checked into git so the whole team — and any agent working in +this repo — benefits from accumulated knowledge. + +This complements Claude Code's built-in per-developer auto-memory: +team-shareable knowledge lives here; personal preferences and ephemeral +state live in the auto-memory. + +## Layout + + .agents/memory/ + ├── MEMORY.md # Index — scan at start of every session + ├── README.md # This file — read when adding/updating memories + ├── feedback/ # Validated patterns & corrections + ├── project/ # Durable project context & rationale + └── reference/ # External systems & resources + +One file per memory. Filename = the memory's kebab-case slug. + +## File format + + --- + name: tests-no-db-mocks + description: One-line summary — used to surface relevance, so be specific. + metadata: + type: feedback # feedback | project | reference + since: 2026-05-19 # date added (ISO) + --- + + + + **Why:** + + **How to apply:** + + Related: [[other-memory-slug]] + +`Why:` and `How to apply:` are required for `feedback` and `project` +memories — they let future readers judge edge cases. `reference` +memories may be shorter (link + one-line purpose). + +Link related memories with `[[slug]]` (the target file's `name:`). + +## Routing — repo vs. auto-memory + +| Kind of fact | Goes to | +|---|---| +| Personal preference, role, style | auto-memory (`user`) | +| Personal habit feedback | auto-memory (`feedback`) | +| Team coding/test/PR rule | **`feedback/`** | +| Durable project rationale | **`project/`** | +| Ephemeral project state (freezes, OOO, deadlines) | auto-memory (`project`) — would rot in git | +| Team-shared external resource | **`reference/`** | +| Personal external resource | auto-memory (`reference`) | + +**Litmus test:** *would a teammate joining the project next month benefit +from knowing this?* If no, it belongs in auto-memory. + +## Write protocol + +1. Write the file **uncommitted** in the working tree. +2. **Surface the change** in the same turn so the human can review. +3. **Do not auto-commit** memory edits as part of an unrelated PR — memory + changes should be reviewable on their own. +4. **Correct in place** when an existing memory turns out wrong; `git blame` + carries the history. +5. **Propose deletion explicitly** when a memory has gone stale, rather + than silently editing it out. + +## Updating the index + +After adding or removing a memory file, update `MEMORY.md`. One line under +the matching section: + + - [slug](category/slug.md) — description from frontmatter + +Keep the index short — long descriptions belong in the file body. + +## Anti-patterns — do not store + +- Anything derivable from the code (module structure, paths, conventions + visible in source). Use `grep` / `Read`. +- Recent-activity summaries or PR lists — `git log` is authoritative. +- Fix recipes for specific bugs — the commit message belongs in the commit. +- Anything already documented in `.agents/` reference docs — keep one + source of truth. +- Personal preferences (see routing). diff --git a/.agents/memory/feedback/.gitkeep b/.agents/memory/feedback/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.agents/memory/project/.gitkeep b/.agents/memory/project/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.agents/memory/reference/.gitkeep b/.agents/memory/reference/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.agents/project-structure-expectations.md b/.agents/project-structure-expectations.md index 81b8e1a62..22a3ab7d6 100644 --- a/.agents/project-structure-expectations.md +++ b/.agents/project-structure-expectations.md @@ -17,5 +17,5 @@ build.gradle.kts # Kotlin-based build configuration settings.gradle.kts # Project structure and settings README.md # Project overview AGENTS.md # Entry point for LLM agent instructions -version.gradle.kts # Declares the project version. +version.gradle.kts # Declares the project version in versioned Gradle Build Tools repos. ``` diff --git a/.agents/skills/dependency-audit/SKILL.md b/.agents/skills/dependency-audit/SKILL.md new file mode 100644 index 000000000..dc52b5246 --- /dev/null +++ b/.agents/skills/dependency-audit/SKILL.md @@ -0,0 +1,115 @@ +--- +name: dependency-audit +description: > + Audit changes to dependency declarations under + `buildSrc/src/main/kotlin/io/spine/dependency/` — catches accidental + version downgrades, BOM mismatches, missing deprecation markers when + artifacts are renamed or removed, copyright drift, and convention drift. + Use whenever a diff touches that directory, or when asked to "audit + this dependency bump". Read-only; does not run builds. +--- + +# Dependency audit (repo-specific) + +You are the dependency auditor for a Spine Event Engine repo. All managed +dependencies live under: + + buildSrc/src/main/kotlin/io/spine/dependency/ + +organized by sub-package: + +- `lib/` — third-party runtime libraries (Kotlin, Guava, Protobuf, gRPC, …). +- `local/` — Spine SDK artifacts (Base, CoreJvm, ModelCompiler, …). +- `test/` — testing libraries (JUnit, Kotest, AssertK, Truth, Jacoco, Kover). +- `build/` — static-analysis and build-time tools (Dokka, ErrorProne, Pmd, + CheckStyle, KSP, …). +- `kotlinx/` — Kotlin-ecosystem libraries (Coroutines, Serialization, + DateTime, AtomicFu). +- `boms/` — BOM declarations. + +Each file declares a Kotlin `object` extending `Dependency` or `DependencyWithBom` +(see `dependency/Dependency.kt`). The shape is: + + object Kotest { + const val version = "6.1.11" + const val group = "io.kotest" + const val assertions = "$group:kotest-assertions-core:$version" + // … + } + +## How to run an audit + +1. **Scope the diff.** + - Run `git diff --stat ...HEAD -- 'buildSrc/src/main/kotlin/io/spine/dependency/**'` + (or `--staged` if the user is mid-commit) and read the file list. + - If the diff is empty, ask the user which files to audit. + +2. **Read each changed file fully.** Don't trust the hunk in isolation — + `version` constants are often referenced elsewhere in the same file (e.g. + `runtimeVersion` reused as `embeddedVersion`). + +3. **Run the checks below in order. Stop the audit and surface a finding the + moment any check fails.** + +## Checks + +### A. Version sanity +- **No silent downgrade.** Compare the old and new `version` value as semver. + A decrease (`2.0.0 -> 1.9.0`) or a snapshot regression (`-SNAPSHOT.183` -> + `.182`) is a Must-fix unless the commit message explicitly justifies it. +- **Snapshot vs. release consistency.** If `version` switches from a release + (`2.0.0`) to a snapshot (`2.0.1-SNAPSHOT.001`), confirm the consuming code + isn't pinned to the release elsewhere via `grep -r ':'`. +- **BOM ↔ component agreement.** For objects extending `DependencyWithBom`, + check that `bom` references the same version as `version` (e.g. Kotlin's + `kotlin-bom:$runtimeVersion`). + +### B. Naming and structure +- **Object name matches the upstream library name** (PascalCase). New files + must follow the convention of neighbors (e.g. `lib/Foo.kt` declares + `object Foo`). +- **No type names in property names** (`fooList`, `barObject`) — this is in + `.agents/coding-guidelines.md`. +- **Module constants use `"$group::$version"`**, not hardcoded + Maven coordinates. Catch copy-paste like `"io.kotest:kotest-assertions-core:6.1.11"`. + +### C. Deprecation discipline +When an artifact is **renamed or removed**: +- The old `const val` must stay with `@Deprecated("…", ReplaceWith("…"))` + or `@Deprecated("…")` (see `Kotest.frameworkApi` and `Kotest.datatest` for + the established style). +- If the diff deletes a `const val` outright, grep the repo with + `git grep ''` to confirm no caller is left behind. If callers exist, + this is a Must-fix. + +### D. Convention drift +- **Copyright header year.** Every changed file should have a current-year + copyright line. If a file was edited but its copyright says `2024`, flag it + (the user can run `/update-copyright` to fix). +- **GitHub URL comment.** New `lib/` and `kotlinx/` files conventionally + start with `// https://github.com//` above the object. + Recommend it if missing. +- **`@Suppress("unused", "ConstPropertyName")` on the object.** This is the + established style for constant-heavy declarations. + +### E. Cross-cutting checks +- **`local/` deps don't leak.** Spine SDK artifacts in `local/` should not be + declared in `lib/` or `test/` (and vice versa). +- **No mixing Groovy and Kotlin DSL.** All Gradle code in `buildSrc/` must be + `.kt` or `.gradle.kts`. Catch any `.gradle` file slipping in. + +## Output format + +Three sections, in this order: + +- **Must fix** — version downgrades, missing deprecation markers on removed + symbols, broken callers, BOM/version mismatches. +- **Should fix** — convention drift, missing deprecation `ReplaceWith`, + missing copyright update, missing URL comment, naming oddities. +- **Nits** — formatting, ordering, doc-comment polish. + +For each finding, cite the file and line, quote the offending lines, and +show the recommended fix. + +End with a one-line verdict: `APPROVE`, `APPROVE WITH CHANGES`, or +`REQUEST CHANGES`. diff --git a/.agents/skills/dependency-update/SKILL.md b/.agents/skills/dependency-update/SKILL.md new file mode 100644 index 000000000..b863a41f0 --- /dev/null +++ b/.agents/skills/dependency-update/SKILL.md @@ -0,0 +1,278 @@ +--- +name: dependency-update +description: > + Walk every dependency declaration under + `buildSrc/src/main/kotlin/io/spine/dependency/`, discover the latest accepted + version of each artifact from the URL hinted in its file (or from Maven + metadata if no URL is present), and update the `version` constant in place. + External dependency scopes accept only released versions; the `local` scope + also accepts snapshots and pre-releases published from sibling Spine repos. + Use when asked to refresh dependency versions, bump libraries, run a + dependency audit, or "see what's stale". +--- + +# Update dependencies + +## Goal + +Bring every dependency object under +`buildSrc/src/main/kotlin/io/spine/dependency/` to its latest accepted version. +For every scope except `local/`, that means the latest **released** version: +snapshots, release candidates, milestones, alpha/beta, EAP, and `-dev` builds +are **excluded**. + +`local/` is the deliberate exception. It holds Spine SDK dependencies published +from sibling Spine repositories, and it may move to newer snapshots or +pre-releases such as `2.0.0-SNAPSHOT.388` or `2.1.0-RC1`. + +The authoritative version source for each artifact is the web page already +referenced in its file. When the file has no URL, use the Maven metadata +fallback described below. For non-`local/` artifacts, a discovered Maven +Central URL is **added back to the file** as a line comment so the next run has +a hint. + +## Inputs + +- No arguments → scan all of `buildSrc/src/main/kotlin/io/spine/dependency/`. +- One or more paths or sub-package names (`lib`, `local`, `test`, `build`, + `kotlinx`, `boms`) → restrict the scan to those. +- `--dry-run` → discover and report, but do not edit. + +## Pre-flight + +1. Run `git status --short`. If the worktree is dirty in files this skill will + touch, stop and ask the user. Otherwise preserve unrelated changes. +2. Confirm `buildSrc/src/main/kotlin/io/spine/dependency/` exists. +3. Note the current branch — every change this skill makes is a candidate for + a single `chore(deps): refresh external versions` commit at the end; the + skill itself does NOT commit. The user decides. + +## Per-file workflow + +For each `*.kt` file in scope: + +### 1. Parse the file + +A dependency file declares one or more Kotlin `object`s, typically extending +`Dependency` or `DependencyWithBom`. The shape is: + + object Kotest { + const val version = "6.1.11" + const val group = "io.kotest" + const val assertions = "$group:kotest-assertions-core:$version" + // … + } + +Extract: + +- `objectName` — the outer `object` identifier. +- `version` — the literal version string. Some files have **multiple** version + constants (`runtimeVersion`, `embeddedVersion`, `annotationsVersion`); treat + each separately. The one driving the artifact is typically `override val + version = …` or the `const val version = …` declared at the top. +- `group` — the Maven group. +- `module` artifact names — each `const val foo = "$group:foo:$version"` line + contributes one artifact name. Use the first one to query Maven Central if + needed for non-`local/` artifacts, or Spine SDK Maven repositories for + `local/` artifacts. +- `versionUrl` — a URL hint. Look in this order: + 1. Line comments above the object: `^//\s*(https?://\S+)`. + 2. KDoc `@see ` inside the object's KDoc. + 3. Plain `@see https?://…` inside the KDoc. + 4. If none: leave `versionUrl` empty and use the Maven metadata fallback + below. + +Skip files that contain only abstract base classes or helpers (`Dependency.kt`, +`DependencyWithBom.kt`, `BomsPlugin.kt`, anything without a concrete artifact +declaration). + +### 2. Find the latest accepted version + +The discovery rule depends on the URL shape. For files under +`dependency/local/`, check the Spine SDK Maven metadata before GitHub, even +when the file has a GitHub URL; snapshots are usually visible in Maven +metadata, not in GitHub's latest-release redirect. + +**A. GitHub repository URL** (`https://github.com//`): + +- Outside `local/`, resolve + `https://github.com///releases/latest`. GitHub redirects to the + latest non-prerelease tag. Read the redirected location or the rendered HTML + to extract the tag. +- In `local/`, do **not** rely on `/releases/latest`, because it hides + pre-releases. Use GitHub releases and tags only after checking Spine SDK + Maven metadata. When you do use GitHub, include pre-release entries and keep + version-like tags that match the artifact. +- Tags often have a `v` prefix. Strip it. +- If the repo publishes per-component tags (e.g. + `kotlinx-coroutines-1.10.2`), prefer the tag whose name matches the + artifact's module identifier. Otherwise take the topmost release. + +**B. Maven Central artifact URL** +(`https://search.maven.org/artifact//` or +`https://repo1.maven.org/maven2///`): + +- Hit Maven Central's REST API: + `https://search.maven.org/solrsearch/select?q=g:+AND+a:&rows=20&core=gav` +- Outside `local/`, filter the `response.docs[].v` values by the pre-release + rule (below). +- In `local/`, keep snapshots and pre-releases in the candidate list. +- Take the highest by semver comparison. + +**C. Spine SDK Maven repositories for `local/` artifacts**: + +- For files under `dependency/local/`, query Maven metadata in the current + Spine SDK Artifact Registry repositories before falling back elsewhere: + - `https://europe-maven.pkg.dev/spine-event-engine/releases` + - `https://europe-maven.pkg.dev/spine-event-engine/snapshots` +- Build the metadata URL as + `///maven-metadata.xml`, where `groupPath` is the + Maven group after first resolving symbolic aliases used in dependency files + (for example, `Spine.group` -> `io.spine` and `Spine.toolsGroup` -> + `io.spine.tools`) and then replacing dots with slashes. +- Read `...` entries. For `local/`, do not + reject `SNAPSHOT`, RC, milestone, alpha, beta, EAP, pre, or dev versions. +- If both release and snapshot repositories have candidates, compare all of + them together and take the highest version. + +**D. Project homepage** (e.g. `https://kotest.io/`, `https://junit.org/`, +`https://www.detekt.dev/`): + +- Try to find a "latest release" or "download" link on the page. If the page + is a thin landing page with no usable version data, fall through to E. + +**E. No URL or unusable URL — Maven metadata fallback**: + +- Outside `local/`, query Maven Central as in B using the file's `group` and + the first module artifact name (the part after `$group:`). +- In `local/`, query the Spine SDK Maven metadata first. Use Maven Central only + if the artifact is absent from those repositories. +- If a non-`local/` Maven Central fallback query returns results, **also insert + a line comment** + `// https://search.maven.org/artifact//` above the object + declaration (after any existing copyright header). This back-fills the URL + hint for next time. Match the existing comment style (one line, no trailing + punctuation). +- If all fallback queries have no result, leave the file untouched and add it + to the `Manual review` section of the final report. + +### 3. Filter pre-releases outside `local/` + +Apply this filter only to files outside `dependency/local/`. + +For `local/` files, snapshots and pre-releases are accepted candidates. Do not +put them in `Filtered pre-releases`; put them in the `local/` confirmation +section of the final report instead. + +Reject any version string matching, case-insensitively: + + -SNAPSHOT$ + -RC[\d\-.]*$ e.g. -RC1, -RC.2 + -M\d+$ e.g. -M3 + -alpha[\d\-.]*$ + -beta[\d\-.]*$ + -EAP[\d\-.]*$ + -pre[\d\-.]*$ + -dev[\d\-.]*$ + \.Beta\d*$ Spring-style trailing tokens + \.Alpha\d*$ + \.RC\d*$ + \.M\d+$ + +Apply the regex to the **suffix after the numeric version**. The version +`2.0.0-SNAPSHOT.182` is a snapshot and must be rejected as a target outside +`local/`, but it is valid for `local/` dependency objects. This skill only +edits dependency files, never `version.gradle.kts` (that belongs to the +`bump-version` skill). + +### 4. Compare versions + +Use semver comparison: + +- Split on `.` and `-`. +- Numeric segments compare numerically; non-numeric segments compare + lexicographically. +- A version without any pre-release suffix is greater than one with the same + numeric prefix but a pre-release suffix. + +Only update when `latest > current`. Equal or lower → no change. + +### 5. Apply the edit + +- Replace the `version` literal with the new value. Use a precise replacement + anchored on the full line (`const val version = ""` → + `const val version = ""`). Do not blindly replace the version string, + because the same string can appear in module URLs constructed via + interpolation (`"$group:…:$version"`) — those will pick up the new value + automatically. +- If the file uses a renamed version constant (`runtimeVersion`, + `compilerVersion`, etc.) that feeds `override val version = compilerVersion`, + update the **source** constant, not the alias. +- For `DependencyWithBom` objects, verify the `bom` line still resolves + correctly. The conventional shape is + `override val bom = "$group:-bom:$version"`, in which case no + separate edit is needed. If the BOM version is hard-coded, update it too. +- Preserve indentation, comment style, and surrounding blank lines exactly. + +### 6. Watch for `local/` artifacts + +`local/` holds Spine SDK dependencies (Base, CoreJvm, ModelCompiler, …) that +are published from sibling Spine repos. This scope accepts snapshots and +pre-releases because these artifacts often advance through internal snapshot +builds before a stable SDK release. + +Still **flag every `local/` update in the report**, and note whether the target +is a release, snapshot, or pre-release. The user can then decide whether to +bump the SDK in lockstep with the rest of the project. Spine SDK artifacts +often need to move together; one-off bumps can cause runtime ABI mismatches. + +## Report + +When the run completes, emit a Markdown report with these sections: + +- **Updated** — table of `file | objectName | old → new | source URL`. +- **Already current** — file/object pairs whose version was already the + newest accepted version. +- **Skipped (no URL, metadata empty)** — manual review needed. +- **Filtered pre-releases** — newer versions found but rejected because they + were RC/SNAPSHOT/alpha/etc. Applies only outside `local/`. +- **`local/` bumps to confirm** — every `local/` change called out separately, + including snapshot and pre-release targets. + +End with the suggested next steps: + +1. Review the diff (`git diff buildSrc/src/main/kotlin/io/spine/dependency/`). +2. Run `./gradlew build` (or `./gradlew clean build` if `.proto` files + participate). +3. If any `local/` artifacts moved, run `./gradlew buildDependants` (the + `ConfigTester` task) to confirm downstream repos still build. +4. Commit. The conventional message is + `chore(deps): refresh external versions` (or a more specific subject if + the diff is small). + +## Safety + +- Do not commit. Do not push. Editing files is the limit of this skill's + authority. +- Never edit `version.gradle.kts` — that's the `bump-version` skill's + responsibility. +- Never auto-resolve a Maven Central query that returns multiple matching + artifacts with different groups (e.g. a library that exists under both + `io.netty` and `io.netty.incubator`). Ask the user. +- If a discovered "latest" version is more than one **major** ahead of the + current value (e.g. `1.x` → `3.x`), flag it as a major bump in the report + and apply the edit only if the user confirms, or only when running + non-interactively with `--include-majors`. Major bumps frequently break + ABI. + +## Failure modes to expect + +- **GitHub rate limit** on the unauthenticated REST API. The `/releases/latest` + HTML page does not require auth and is the preferred fallback. +- **Per-component tags** in a monorepo. Match by artifact name, don't take the + topmost tag blindly. +- **Repositories that publish to JCenter only** — JCenter is sunset; if Maven + Central is empty, the dependency may need migration. Flag it. +- **Vendor-specific version schemes** (e.g. dates: `2025.10.01`) — the + semver comparator above will still order these correctly; just don't + mis-classify them as pre-releases. diff --git a/.agents/skills/dependency-update/agents/openai.yaml b/.agents/skills/dependency-update/agents/openai.yaml new file mode 100644 index 000000000..a61198d32 --- /dev/null +++ b/.agents/skills/dependency-update/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Dependency Update" + short_description: "Refresh dependency versions, allowing snapshots only for local Spine SDK artifacts" + default_prompt: "Use $dependency-update to walk every dependency object under buildSrc/src/main/kotlin/io/spine/dependency/, find the latest accepted version for each, and update the version constants in place. External scopes use released non-snapshot versions only; dependency/local/ may use snapshots and pre-releases from sibling Spine repos. Use the URL referenced in each file as the source of truth; fall back to Maven metadata and back-fill missing hints when useful." diff --git a/.agents/skills/kotlin-review/SKILL.md b/.agents/skills/kotlin-review/SKILL.md new file mode 100644 index 000000000..c55c8c49c --- /dev/null +++ b/.agents/skills/kotlin-review/SKILL.md @@ -0,0 +1,62 @@ +--- +name: kotlin-review +description: > + Review Kotlin (and Java) changes in this repo against the Spine coding + guidelines, safety rules, and testing policy. Use after any non-trivial + code edit, before opening a PR, or when asked for a code review. + Read-only; does not run builds. +--- + +# Kotlin code review (repo-specific) + +You are the Kotlin reviewer for this repository. The authoritative standards +live in `.agents/`: + +- `.agents/coding-guidelines.md` — Kotlin idioms, formatting, what to prefer/avoid. +- `.agents/safety-rules.md` and `.agents/advanced-safety-rules.md` — hard constraints + (no reflection without approval, no analytics/telemetry, no blocking calls in + coroutines, no auto-updating external dependencies). +- `.agents/testing.md` — Kotest assertions preferred, stubs not mocks. +- `.agents/project-structure-expectations.md` — module/source-set layout. +- `.agents/version-policy.md` — version bumps are required only when the + repository has a root `version.gradle.kts`. + +## Review procedure + +1. Read the diff. Use `git diff --staged` or `git diff ...HEAD` depending on + what the user describes. Do NOT review the full repo — only what changed. +2. Read each affected file fully, not just the diff hunks. Smart casts, + nullability, and idiomatic refactors require surrounding context. +3. Check against `.agents/coding-guidelines.md`: + - Kotlin idioms (extension functions, `when`, smart casts, data/sealed classes). + - Immutability by default. + - No `!!` without justification. + - No type names in variable names. + - No string duplication — use companion-object constants. + - No mixing Groovy/Kotlin DSL in build logic. + - No double empty lines (collapse to a single empty line); no trailing whitespace. +4. Check safety rules: reflection, telemetry, blocking-in-coroutines, dependency + bumps that weren't requested. +5. Check tests: every functional change should have tests using Kotest assertions + and stubs (not mocks). +6. Check the version gate: + - If the repository has a root `version.gradle.kts`, confirm it was + incremented when the change is user-visible. + - If root `version.gradle.kts` is absent at both the base ref and `HEAD`, + the version check is not applicable. Do not report a missing version bump + or ask for the file to be created. + +## Output format + +Return three sections, in this order: + +- **Must fix** — violations of safety rules, broken builds, missing version + bump when the version gate applies, missing tests for functional changes. +- **Should fix** — coding-guideline violations and clearer idiomatic alternatives. + Cite the specific guideline. +- **Nits** — style and naming suggestions. + +For each item, quote the file and line, show the current code, and show the +recommended replacement. If there's nothing in a section, write "None." + +End with a one-line verdict: `APPROVE`, `APPROVE WITH CHANGES`, or `REQUEST CHANGES`. diff --git a/.agents/skills/pre-pr/SKILL.md b/.agents/skills/pre-pr/SKILL.md new file mode 100644 index 000000000..ca09c41ff --- /dev/null +++ b/.agents/skills/pre-pr/SKILL.md @@ -0,0 +1,173 @@ +--- +name: pre-pr +description: > + Run the pre-PR checklist for this repo: apply the version gate only when + the repository has a root `version.gradle.kts`, run the configured + build/check command per `.agents/running-builds.md`, and invoke the + configured reviewers (`kotlin-review`, `review-docs`, `dependency-audit`) + against the branch diff. On success, write a sentinel file at + `.git/pre-pr.ok` so the `gh pr create` hook can verify the checklist ran + for the current HEAD. Use before opening a PR, or when CI rejected a + branch and you want a fast local repro. +--- + +# Pre-PR checklist (repo-specific) + +You are the pre-PR gate for this repository. You compose the existing +reviewers and the documented repository rules into a single pass that must +succeed before a pull request is opened. + +This skill supports both versioned Gradle Build Tools projects and repositories +that intentionally do not have `version.gradle.kts` (for example, shared +configuration repositories). Do not create `version.gradle.kts` just to satisfy +this checklist. When the file is absent from the project root, the version-bump +check is **not applicable**. + +The authoritative standards live in `.agents/`: + +- `.agents/version-policy.md` — applies only when the repository has a root + `version.gradle.kts`. +- `.agents/running-builds.md` — which build/check command to run based on what + changed. It may be Gradle or another repository-specific command. +- `.agents/safety-rules.md` and `.agents/advanced-safety-rules.md` — hard + constraints checked by the reviewers. +- The reviewer skills/agents themselves: `kotlin-review` (Claude agent), + `review-docs` (skill + Claude agent), `dependency-audit` (Claude agent). + +## Procedure + +Execute the steps in order. If a step fails, stop, write a `FAIL` sentinel +(see step 6), and report the failure — do not run the remaining steps. + +### 1. Determine scope and repository capabilities + +- Base ref: `master` unless the user provides a different one. +- Diff command: `git diff ...HEAD --name-only` for the file list, + `git diff ...HEAD --stat` for the summary. +- Repository root: `git rev-parse --show-toplevel`. +- Version gate: + - Check only the repository-root `version.gradle.kts`. + - If `version.gradle.kts` is absent at both `` and `HEAD`, record the + version check as `N/A` and continue. Do not ask the user to run + `/bump-version`. + - If `version.gradle.kts` exists at `HEAD`, enforce the version check in + step 2. + - If `version.gradle.kts` exists at `` but is missing at `HEAD`, fail + unless the user explicitly asked to migrate the repository away from + Gradle Build Tools versioning. +- Classify the changes: + - **proto** — any `*.proto` file changed. + - **code** — any `*.kt`, `*.kts`, or `*.java` file changed. + - **docs** — any `*.md` file or doc-only edits inside sources changed. + - **deps** — any file under `buildSrc/src/main/kotlin/io/spine/dependency/` + changed. + +### 2. Version-bump check + +- If the version gate is `N/A`, skip this step with note: + "`version.gradle.kts` is absent; this repository is not a versioned Gradle + Build Tools project." +- Otherwise, read `version.gradle.kts` at `HEAD` and, when present, at + ``. +- Confirm the version string is strictly greater (semver + Spine snapshot + rules — see `.agents/version-policy.md`) when both sides have the file. +- If the file is newly introduced at `HEAD`, report the introduced version and + continue. +- If unchanged or decreased, stop with a Must-fix: "Run `/bump-version`." + +### 3. Build or check + +Pick the target per `.agents/running-builds.md`: + +- **proto** changed → `./gradlew clean build` +- Else **code** changed → `./gradlew build` +- Else **docs**-only → `./gradlew dokka` (tests not required) + +If the repository does not have `./gradlew`, do not fail solely because Gradle +is unavailable. Read `.agents/running-builds.md` for the repository-specific +non-Gradle command that matches the change type, and run that instead. If no +build/check command is documented for the change type, record `build=skipped` +with the reason and continue. + +Run the chosen command. Surface the first failing module/task/check. On +failure, stop and write a `FAIL` sentinel. + +### 4. Reviewers (run in parallel) + +Dispatch the relevant reviewers concurrently and collect their verdicts: + +- Always: `kotlin-review` (if **code** changed) and `review-docs` (if + **docs** or KDoc changed). +- If **deps** changed: `dependency-audit`. + +Pass each reviewer the base ref, changed-file list, build/check result, and +version-check result. When the version check is `N/A`, say explicitly: +"This repository has no root `version.gradle.kts`; a version bump is not +applicable and must not be reported as missing." + +Each reviewer is read-only and emits a Must-fix / Should-fix / Nits +report plus a one-line verdict (`APPROVE`, `APPROVE WITH CHANGES`, or +`REQUEST CHANGES`). + +### 5. Aggregate + +- Overall **PASS** when: + - Version check passed or was `N/A`, + - Build succeeded, + - Every dispatched reviewer returned `APPROVE` or `APPROVE WITH CHANGES` + *and* no Must-fix items remain unaddressed in this session. +- Otherwise **FAIL**. + +### 6. Sentinel + +Write `.git/pre-pr.ok` at the repo root (NOT under `.claude/` — the +sentinel must travel with the local clone, not be checked in). Format: + +``` +head= +branch= +status=PASS|FAIL +timestamp= +build= +reviewers= +version=new, introduced:, or "not-applicable"> +``` + +The `gh pr create` hook (`.claude/scripts/pre-pr-gate.sh`) checks this +file's `head=` and `status=` fields. Extra fields are allowed. The sentinel is +invalidated automatically when HEAD advances — the hook compares the recorded +`head=` against the current HEAD SHA. + +## Output format + +Report in this shape: + +``` +## Pre-PR checklist ( vs ) + +| Check | Status | Notes | +|---------------|--------|----------------------------------------| +| Version check | … | , introduced, or N/A | +| Build/check | … | | +| kotlin-review | … | | +| review-docs | … | | +| dep audit | … | | + +**Overall: PASS|FAIL** +Sentinel: .git/pre-pr.ok (status=PASS|FAIL, head=) +``` + +On `PASS`, end with: "You can now run `gh pr create`." +On `FAIL`, end with the specific blocker and the next action. + +## Notes + +- This skill must NOT create the PR itself. It only gates whether the + workspace is ready. +- This skill must NOT create `version.gradle.kts`. Repositories without a root + `version.gradle.kts` are valid; their version check is `N/A`. +- The sentinel lives under `.git/` (untracked by definition) so it is + per-clone and never committed. +- Each reviewer remains the source of truth for its own checks; this + skill does not duplicate their rules — it only orchestrates and + aggregates. diff --git a/.agents/skills/review-docs/SKILL.md b/.agents/skills/review-docs/SKILL.md new file mode 100644 index 000000000..d936fa28a --- /dev/null +++ b/.agents/skills/review-docs/SKILL.md @@ -0,0 +1,129 @@ +--- +name: review-docs +description: > + Review documentation changes — KDoc/Javadoc inside Kotlin/Java sources and + Markdown docs (`README.md`, `docs/**`) — against Spine documentation + conventions. Use when a diff touches doc comments or Markdown, before + opening a doc-affecting PR, or when asked for a documentation review. + Read-only; does not run builds. +--- + +# Review documentation (repo-specific) + +You are the documentation reviewer for a Spine Event Engine project. You +focus strictly on documentation quality — prose, KDoc/Javadoc, and Markdown — +and deliberately do **not** duplicate the code-review skill (which owns +Kotlin idioms, safety rules, tests, and version-gate checks). + +The authoritative standards live in `.agents/`: + +- `.agents/documentation-guidelines.md` — commenting rules, TODO-comment + format, "file/dir names as code", widow/runt/orphan/river rule (with the + diagram at `.agents/widow-runt-orphan.jpg`). +- `.agents/documentation-tasks.md` — KDoc-example requirement on APIs; + Javadoc → KDoc conversion rules (`

` removal, etc.). +- `.agents/skills/writer/SKILL.md` — Markdown conventions (footnote-style + reference links for external URLs, typographic quotes only on actual + page/section titles, sidenav-sync rules under `docs/`). +- `.agents/running-builds.md` — for doc-only Kotlin/Java changes the right + build is `./gradlew dokka` (no tests required). + +## Review procedure + +1. **Scope the diff.** Obtain the change set via `git diff --staged` or + `git diff ...HEAD` depending on what the user describes. Restrict + to files matching: + - `**/*.kt`, `**/*.kts`, `**/*.java` (for KDoc/Javadoc inside sources) + - `**/*.md` (Markdown docs) + Do **not** review the full repo — only what changed. + +2. **Read each affected file fully, not just the hunks.** Prose review + requires surrounding context — judging widows/runts/orphans, link + placement, and KDoc completeness needs the whole paragraph and the + surrounding declarations. + +3. **Stay in scope.** If you spot a code-quality issue (idiom, naming, + tests, version-gate applicability), note it briefly as a "for the code + reviewer" item under Nits — do not expand the review. + +## Checks + +### A. KDoc / Javadoc inside sources + +- **Public and internal APIs carry KDoc.** Per `documentation-tasks.md`, + KDoc should include at least one usage example for non-trivial APIs. + Missing KDoc on a new or modified public/internal symbol is a Should-fix. +- **No Javadoc residue in Kotlin.** When converting from Java: + - `

` tags on a text line removed (`"

This"` → `"This"`). + - `

` on its own line replaced with a blank line. + - HTML entities (`&`, `<`, …) converted to literals where appropriate. +- **Inline comments in production code are minimized.** Inline comments are + fine in tests; in production source they should explain *why* (a + constraint, invariant, surprise) and never restate *what* the code does. +- **TODO comments follow the Spine format.** Linked from + `documentation-guidelines.md` to the wiki "TODO-comments" page. A bare + `// TODO: …` without owner/issue reference is a Should-fix. +- **File and directory names rendered as code.** Within KDoc/Javadoc prose, + `path/to/file.kt` and `module-name` must use backticks. + +### B. Markdown docs + +- **Footnote-style reference links** for external `https://` URLs (per the + `writer` skill). Inline `[label](https://…)` in body prose is a + Should-fix; inline links to local relative paths are fine. +- **Typographic quotes** (`" "` / `' '`) only when the visible link text is + an actual page or section title (e.g., the "Getting started" page). + Do **not** quote generic phrases like "this page", "the next section", + "What's next", or section numbers (`4.3`). +- **Sidenav sync.** If the diff adds/removes/renames/moves a page under + `docs/content/docs/

/`, the matching current-version + `sidenav.yml` must be updated (see the `writer` skill for how to + identify the current version via `docs/data/versions.yml`). A missing + sidenav update is a Must-fix. +- **Fenced code blocks** for commands and examples — no indented code + blocks for shell snippets (they swallow `$` prompts and hurt copy/paste). +- **Heading hierarchy.** No skipped levels (`#` → `###`); exactly one `#` + per file. + +### C. Prose flow (Spine-specific) + +- **Avoid widows, runts, orphans, and rivers** — the rule from + `documentation-guidelines.md` with the diagram at + `.agents/widow-runt-orphan.jpg`. Operationally: + - **Widow / runt**: a paragraph's last line containing only one short + word (or a hyphenated fragment). Reflow the prior line. + - **Orphan**: a single trailing line of a paragraph stranded at the top + of a new block (often appears after a heading or list). Reflow. + - **River**: a vertical "gap" of aligned spaces running down justified + text. Rare in Markdown but possible in tables — reflow the table or + rewrite to break the alignment. + Quote the offending paragraph and propose a rewording that fixes it. + +### D. Terminology and tone + +- **Match code identifiers verbatim.** When prose references a class, + function, or property, the name in backticks must match the source + exactly (case, plurality). +- **Consistent terminology across the diff.** If the same concept is + named two different ways in the same change set, pick one. + +## Output format + +Three sections, in this order: + +- **Must fix** — broken/missing KDoc on a newly-introduced public API, + missing sidenav sync, broken cross-references, Javadoc residue + (`

` tags) left in Kotlin KDoc, broken Markdown links. +- **Should fix** — TODO format, inline-comment overuse in production, + inline external links that should be footnote-style, missing typographic + quotes (or unwanted ones), widow/runt/orphan/river paragraphs, + fenced-vs-indented code blocks. +- **Nits** — wording, terminology drift, code-identifier capitalization + in prose, "for the code reviewer" pointers if any code issues surfaced + incidentally. + +For each finding, cite the file and line, quote the offending text, and +show the recommended rewrite. If a section is empty, write "None." + +End with a one-line verdict: `APPROVE`, `APPROVE WITH CHANGES`, or +`REQUEST CHANGES`. diff --git a/.agents/tasks/README.md b/.agents/tasks/README.md new file mode 100644 index 000000000..325f52cf5 --- /dev/null +++ b/.agents/tasks/README.md @@ -0,0 +1,128 @@ +# Task plans — `.agents/tasks/` + +Durable task plans. Checked into git so the whole team — and any +agent working in this repo — can review, resume, or pick up +sub-tasks across sessions. + +This complements Claude Code's built-in Plan mode and in-session +task list: the file here is the durable source of truth; the +built-in tools gate approval and render live progress. + +## Layout + + .agents/tasks/ + ├── README.md # This file + └── .md # One file per task; status in frontmatter + +Filename = the task's kebab-case slug. Multiple active tasks per +branch are allowed — use distinct slugs. + +## File format + + --- + slug: add-team-memory + branch: tune-claude + owner: claude # or a human/agent handle + status: in-progress # see status values below + started: 2026-05-19 + related-memories: # optional — links into .agents/memory/ + - team-memory-routing + --- + + ## Goal + + + ## Context + + + ## Plan + - [x] Step 1 + - [ ] Step 2 + - notes / sub-bullets + - [ ] Step 3 + + ## Log + - 2026-05-19 14:02 — drafted, awaiting approval + - 2026-05-19 14:15 — approved, executing + - 2026-05-19 14:42 — step 3 blocked on … + +The checklist uses `- [ ]` / `- [x]` so another agent can claim and +complete unchecked items by ticking them and adding a `Log` line. + +### `status` values + +| value | meaning | +|---|---| +| `draft` | written but not yet approved | +| `approved` | approved, not yet started | +| `in-progress` | execution under way | +| `blocked` | paused; reason in `Log` | +| `in-review` | work done, awaiting review | +| `done` | complete — file is then deleted (see lifecycle) | + +## Workflow + +1. **Discover** — at task start, scan `.agents/tasks/` for + in-progress or blocked plans on the current branch. Resume + rather than restart. +2. **Draft** — write `.md` with `status: draft` and the + plan checklist. +3. **Approval gate** — `EnterPlanMode` → `ExitPlanMode`. The plan + presented to the human references the file path; the human may + edit the file directly before approving. +4. **Mirror** — on approval, flip `status: approved` → `in-progress` + and populate `TaskCreate` from the top-level checklist for live + in-session progress. +5. **Execute + sync** — use `TaskUpdate` for fine-grained progress. + Edit the file only at meaningful checkpoints: step done, blocker, + scope change, new note. +6. **Complete** — flip `status: done`. The file is raw material for + the PR description. +7. **Delete on merge** — once the branch lands on master, delete the + task file in the same commit or shortly after. `git log --follow` + recovers it if ever needed. + +## Cross-agent coordination + +- Other agents (or other CC sessions) `Read` the file to pick up + state. They MUST update `status`, tick checkboxes, and append + `Log` lines rather than rewriting the plan silently. +- If two agents work the same task in parallel, partition by + checkbox — each agent claims unchecked items by tagging the line + (e.g. `- [ ] (owner: reviewer-bot) Run dependency-audit`) or by + appending a `Log` line. +- The **file** is the contract. In-session `TaskCreate` state is + per-session and not authoritative. + +## When to create a task file + +Create one whenever the work is non-trivial: + +- Changes spanning multiple files or modules (features, refactors). +- Lengthy documentation work — multi-page guides, restructuring + `docs/`, migration notes, tutorials. The task file plans and + tracks the effort; the docs-related skills (`writer`, + `write-docs`, `review-docs`) handle individual page work inside + the plan steps. +- Cross-agent or cross-session work (e.g. one agent drafts, another + reviews). +- Anything that may span sessions and needs durable state. + +Do **not** create a task file for: + +- Trivial changes (single-file edits, typo fixes, version bumps) — + pure overhead. +- Deliverables themselves — code lives in source, docs in `docs/`, + design records where the project keeps them. Task files describe + the *work*, not the artifact. +- Status reports of work already done — that's a `Log` entry on an + existing task, or the PR description. +- Personal reminders / todo lists — use the built-in task list. + +## Relationship to other stores + +- **`.agents/memory/`** — enduring lessons that survive *across* + tasks. If a task yields a generalizable rule, add the memory and + link from the task's `related-memories`. +- **Built-in auto-memory** — personal and ephemeral. Task files do + not carry personal preferences. diff --git a/.agents/version-policy.md b/.agents/version-policy.md index 95ac4513e..3e8abd549 100644 --- a/.agents/version-policy.md +++ b/.agents/version-policy.md @@ -1,15 +1,19 @@ # Version policy -The project follows the [Spine SDK Versioning policy][wiki-versioning]. -The version is kept in `version.gradle.kts` at the project root and follows -[Semantic Versioning 2.0.0][semver] with Spine-specific extensions -(snapshot `NUMBER`, patch, and flavor suffixes). +When a repository has `version.gradle.kts` at the project root, it follows the +[Spine SDK Versioning policy][wiki-versioning]. The version is kept in that +file and follows [Semantic Versioning 2.0.0][semver] with Spine-specific +extensions (snapshot `NUMBER`, patch, and flavor suffixes). -PRs without a version bump fail CI. +For repositories with root `version.gradle.kts`, PRs without a version bump +fail CI. Repositories without that file are not versioned Gradle Build Tools +projects; their version check is not applicable, and agents must not create +`version.gradle.kts` just to satisfy `/pre-pr`. -For the bump procedure — version-number selection, the commit-message -convention, the rebuild, dependency-report updates, and conflict resolution — -use the [`bump-version`](skills/bump-version/SKILL.md) skill. +For the bump procedure in repositories that have root `version.gradle.kts` — +version-number selection, the commit-message convention, the rebuild, +dependency-report updates, and conflict resolution — use the +[`bump-version`](skills/bump-version/SKILL.md) skill. [semver]: https://semver.org/ [wiki-versioning]: https://github.com/SpineEventEngine/documentation/wiki/Versioning diff --git a/.claude/agents/dependency-audit.md b/.claude/agents/dependency-audit.md new file mode 100644 index 000000000..109456b83 --- /dev/null +++ b/.claude/agents/dependency-audit.md @@ -0,0 +1,15 @@ +--- +name: dependency-audit +description: Audits changes to dependency declarations under `buildSrc/src/main/kotlin/io/spine/dependency/` — catches accidental version downgrades, BOM mismatches, missing deprecation markers, copyright drift, and convention drift. Use proactively whenever a diff touches that directory, or when the user asks "audit this dependency bump". Read-only; does not run builds. +tools: Read, Grep, Glob, Bash +model: inherit +--- + +Follow the `dependency-audit` skill exactly: + +- Skill: `.agents/skills/dependency-audit/SKILL.md` +- The skill owns the per-area checks (version sanity, naming and structure, + deprecation discipline, convention drift, cross-cutting) and the output + format (Must fix / Should fix / Nits + one-line verdict). +- Read-only: use `Read`, `Grep`, `Glob`, and `Bash` solely for `git diff`, + `git grep`, and related read-only inspection. Do not run builds. diff --git a/.claude/agents/kotlin-review.md b/.claude/agents/kotlin-review.md new file mode 100644 index 000000000..74583aa33 --- /dev/null +++ b/.claude/agents/kotlin-review.md @@ -0,0 +1,17 @@ +--- +name: kotlin-review +description: Reviews Kotlin (and Java) changes against Spine coding guidelines, safety rules, and testing policy. Use proactively after any non-trivial code edit, before opening a PR, or when the user asks for a code review. Read-only; does not run builds. +tools: Read, Grep, Glob, Bash +model: inherit +--- + +Follow the `kotlin-review` skill exactly: + +- Skill: `.agents/skills/kotlin-review/SKILL.md` +- The skill owns the procedure, the checks (Kotlin idioms, safety rules, + testing policy, version-gate applicability), and the output format + (Must fix / Should fix / Nits + one-line verdict). +- Stay in scope: code only. If a documentation issue surfaces, note it + briefly as a Nit pointing at the `review-docs` agent. +- Read-only: use `Read`, `Grep`, `Glob`, and `Bash` solely for `git diff` + and related read-only inspection. Do not run builds. diff --git a/.claude/agents/review-docs.md b/.claude/agents/review-docs.md new file mode 100644 index 000000000..0481b240b --- /dev/null +++ b/.claude/agents/review-docs.md @@ -0,0 +1,18 @@ +--- +name: review-docs +description: Reviews documentation changes — KDoc/Javadoc inside Kotlin/Java sources and Markdown docs (`README.md`, `docs/**`) — against Spine documentation conventions. Use proactively when a diff touches doc comments or Markdown, before opening a doc-affecting PR, or when the user asks for a documentation review. Read-only; does not run builds. +tools: Read, Grep, Glob, Bash +model: inherit +--- + +Follow the `review-docs` skill exactly: + +- Skill: `.agents/skills/review-docs/SKILL.md` +- The skill owns the review procedure, the per-area checks (KDoc/Javadoc, + Markdown, prose flow, terminology), and the output format + (Must fix / Should fix / Nits + one-line verdict). +- Scope yourself to documentation only. If you spot a code-quality issue, + surface it briefly as a Nit pointing at the `kotlin-review` agent — + do not expand the review. +- Read-only: use `Read`, `Grep`, `Glob`, and `Bash` solely for `git diff` + and related read-only inspection. Do not run builds. diff --git a/.claude/commands/bump-gradle.md b/.claude/commands/bump-gradle.md new file mode 100644 index 000000000..f9078802c --- /dev/null +++ b/.claude/commands/bump-gradle.md @@ -0,0 +1,13 @@ +--- +description: Upgrade the Gradle wrapper to the latest release. +argument-hint: "[gradle-version]" +allowed-tools: Read, Edit, Bash(./gradlew:*), Bash(git status:*), Bash(git diff:*), WebFetch +--- + +Follow the `bump-gradle` skill exactly: + +- Skill: `.agents/skills/bump-gradle/SKILL.md` +- Read the skill first. +- Use https://docs.gradle.org/current/release-notes.html as the source of truth for the latest version. Do NOT rely on remembered Gradle versions. +- If the user supplied a version: $ARGUMENTS, use it; otherwise read it from the release notes. +- Commit the wrapper change and dependency report change in separate commits per the skill. diff --git a/.claude/commands/bump-version.md b/.claude/commands/bump-version.md new file mode 100644 index 000000000..82e18b599 --- /dev/null +++ b/.claude/commands/bump-version.md @@ -0,0 +1,13 @@ +--- +description: Bump the project version in version.gradle.kts per Spine SDK versioning policy. +argument-hint: "[snapshot|minor|major]" +allowed-tools: Read, Edit, Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(./gradlew:*) +--- + +Follow the `bump-version` skill exactly: + +- Skill: `.agents/skills/bump-version/SKILL.md` +- Read the skill first; it owns the policy (snapshot numbering, version conflicts, rebuilding reports). +- Increment requested by the user: $ARGUMENTS (treat as "snapshot" if empty). +- Inspect `git status --short` before editing; preserve unrelated user changes. +- Stop and ask the user before committing. diff --git a/.claude/commands/dependency-update.md b/.claude/commands/dependency-update.md new file mode 100644 index 000000000..9c54da149 --- /dev/null +++ b/.claude/commands/dependency-update.md @@ -0,0 +1,16 @@ +--- +description: Refresh external dependency versions in buildSrc to their latest non-snapshot release. +argument-hint: "[--dry-run] [paths...]" +allowed-tools: Read, Edit, Write, Grep, Glob, WebFetch, Bash(git status:*), Bash(git diff:*), Bash(./gradlew build:*), Bash(./gradlew clean build:*) +--- + +Follow the `dependency-update` skill exactly: + +- Skill: `.agents/skills/dependency-update/SKILL.md` +- Scope / flags: $ARGUMENTS +- Walk every dependency object under `buildSrc/src/main/kotlin/io/spine/dependency/`. +- Source of truth per artifact: the URL in the file's comment (line `// https://...` or KDoc `@see`). If no URL, fall back to Maven Central AND back-fill the URL comment. +- Filter out snapshots, RCs, alphas, betas, milestones, EAPs, and `-dev` builds. +- Apply the edit. Do NOT commit; emit the report described in the skill. +- Flag `local/` (Spine SDK) updates separately so the user can decide whether to bump in lockstep. +- After the run, suggest the user review the diff and run `./gradlew build` (or `./gradlew clean build` if proto files participate). For `local/` bumps, suggest `./gradlew buildDependants`. diff --git a/.claude/commands/java-to-kotlin.md b/.claude/commands/java-to-kotlin.md new file mode 100644 index 000000000..6f2c072f9 --- /dev/null +++ b/.claude/commands/java-to-kotlin.md @@ -0,0 +1,13 @@ +--- +description: Convert Java files to idiomatic Kotlin, including Javadoc -> KDoc. +argument-hint: "" +allowed-tools: Read, Edit, Write, Bash(./gradlew:*), Bash(git status:*), Grep, Glob +--- + +Follow the `java-to-kotlin` skill exactly: + +- Skill: `.agents/skills/java-to-kotlin/SKILL.md` +- Target: $ARGUMENTS +- Preserve behavior. Convert Javadoc to KDoc, `@Nullable` to nullable Kotlin types, getters/setters to properties, static methods to companion objects or top-level functions. +- After each file, run `./gradlew compileKotlin` (or the relevant module's compile task) to verify. +- Honor `.agents/coding-guidelines.md` for Kotlin idioms. diff --git a/.claude/commands/move-files.md b/.claude/commands/move-files.md new file mode 100644 index 000000000..25885f9d7 --- /dev/null +++ b/.claude/commands/move-files.md @@ -0,0 +1,12 @@ +--- +description: Move or rename files/directories, updating all references and build metadata. +argument-hint: " " +allowed-tools: Read, Edit, Bash(git mv:*), Bash(git status:*), Bash(git ls-files:*), Grep, Glob +--- + +Follow the `move-files` skill exactly: + +- Skill: `.agents/skills/move-files/SKILL.md` +- Operation: $ARGUMENTS +- Preflight (run `git status --short`, classify scope) -> Search for all old identifiers -> Move with `git mv` -> Repair references (imports, build metadata, docs) -> Verify. +- Report: Moved[], UpdatedRefs[], Verification[], Risks[]. diff --git a/.claude/commands/pre-pr.md b/.claude/commands/pre-pr.md new file mode 100644 index 000000000..24499cc51 --- /dev/null +++ b/.claude/commands/pre-pr.md @@ -0,0 +1,32 @@ +--- +description: Run the applicable pre-PR checklist (version gate, build/check, reviewers) and write a sentinel so `gh pr create` is unblocked. +argument-hint: "[base-ref]" +allowed-tools: Read, Write, Grep, Glob, Agent, Bash +--- + +Follow the `pre-pr` skill exactly: + +- Skill: `.agents/skills/pre-pr/SKILL.md` +- Base ref: $ARGUMENTS (treat empty as `master`). +- Detect whether the repository-root `version.gradle.kts` exists. If it is + absent at both the base ref and `HEAD`, the version check is `N/A`; do not + create the file and do not ask for `/bump-version`. +- Run the build/check command selected by the skill and + `.agents/running-builds.md`. The command may be Gradle or non-Gradle. +- Dispatch the reviewers as Claude subagents in parallel — send a single + message with multiple Agent tool uses: + - `kotlin-review` when `.kt|.kts|.java` files changed. + - `review-docs` when `.md` files or KDoc inside sources changed. + - `dependency-audit` when any file under + `buildSrc/src/main/kotlin/io/spine/dependency/` changed. +- Pass the version-check status to reviewers. If it is `N/A`, tell them: + "This repository has no root `version.gradle.kts`; a version bump is not + applicable and must not be reported as missing." +- Each reviewer is read-only; do not pass it edit tools. +- On any reviewer returning `REQUEST CHANGES`, treat the overall result + as `FAIL` and stop before writing the sentinel as `PASS`. +- Sentinel location: `$(git rev-parse --show-toplevel)/.git/pre-pr.ok`, + format per the skill (`head=`, `branch=`, `status=`, `timestamp=`, + `build=`, `reviewers=`, `version=`). Use `git rev-parse HEAD` for the + SHA and `date -u +%Y-%m-%dT%H:%M:%SZ` for the timestamp. +- Do NOT run `gh pr create`. That is the user's next step. diff --git a/.claude/commands/review-docs.md b/.claude/commands/review-docs.md new file mode 100644 index 000000000..f8043f0ea --- /dev/null +++ b/.claude/commands/review-docs.md @@ -0,0 +1,21 @@ +--- +description: Review documentation changes (KDoc/Javadoc and Markdown) against Spine documentation conventions. +argument-hint: "[base-ref | --staged | paths...]" +allowed-tools: Read, Grep, Glob, Bash(git diff:*), Bash(git log:*), Bash(git status:*), Bash(git rev-parse:*), Bash(git ls-files:*) +--- + +Follow the `review-docs` skill exactly: + +- Skill: `.agents/skills/review-docs/SKILL.md` +- Scope / flags: $ARGUMENTS + - Empty: review the current branch's diff against `master` (`git diff master...HEAD`). + - `--staged`: review staged changes only (`git diff --staged`). + - A base ref (e.g. `master`, `origin/master`, a commit SHA): review `git diff ...HEAD`. + - Explicit paths: limit the review to those paths in addition to the diff scope. +- The skill owns the procedure, the per-area checks (KDoc/Javadoc, Markdown, + prose flow, terminology), and the output format (Must fix / Should fix / + Nits + one-line verdict). +- Stay in scope: documentation only. If a code-quality issue surfaces, + note it briefly as a Nit pointing at `/review` (or the `kotlin-review` + agent) — do not expand the review. +- Read-only: do not edit files, do not run builds. diff --git a/.claude/commands/run-build.md b/.claude/commands/run-build.md new file mode 100644 index 000000000..8a8d84ca0 --- /dev/null +++ b/.claude/commands/run-build.md @@ -0,0 +1,12 @@ +--- +description: Build the project the right way based on what changed (proto vs. Kotlin/Java vs. docs). +allowed-tools: Bash(./gradlew:*), Bash(git status:*), Bash(git diff:*) +--- + +Decide which build to run by looking at `git status --short` and `git diff --name-only`: + +- If any `.proto` files changed: `./gradlew clean build` +- Else if Kotlin or Java source changed: `./gradlew build` +- Else if only docs/comments changed (KDoc / Javadoc / Markdown): `./gradlew dokka`. Tests are NOT required for doc-only changes. + +Report the chosen command and its result. See `.agents/running-builds.md`. diff --git a/.claude/commands/update-copyright.md b/.claude/commands/update-copyright.md new file mode 100644 index 000000000..076fb6133 --- /dev/null +++ b/.claude/commands/update-copyright.md @@ -0,0 +1,12 @@ +--- +description: Refresh copyright headers from the IntelliJ profile, replacing today.year with the current year. +argument-hint: "[paths...]" +allowed-tools: Bash(python3 .agents/skills/update-copyright/scripts/update_copyright.py:*), Read +--- + +Follow the `update-copyright` skill exactly: + +- Skill: `.agents/skills/update-copyright/SKILL.md` +- Run: `python3 .agents/skills/update-copyright/scripts/update_copyright.py $ARGUMENTS` +- If $ARGUMENTS is empty, run once with `--dry-run`, show the output to the user, then run without `--dry-run`. +- Never add a header to a file that doesn't already have one. diff --git a/.claude/commands/write-docs.md b/.claude/commands/write-docs.md new file mode 100644 index 000000000..b9b9a742b --- /dev/null +++ b/.claude/commands/write-docs.md @@ -0,0 +1,14 @@ +--- +description: Write or update Markdown / KDoc documentation per Spine documentation conventions. +argument-hint: "" +allowed-tools: Read, Edit, Write, Grep, Glob +--- + +Follow the `writer` skill exactly: + +- Skill: `.agents/skills/writer/SKILL.md` +- Topic / target: $ARGUMENTS +- Decide audience first (end user, contributor, maintainer, tooling). +- Prefer updating an existing doc over creating a new one. +- Keep `docs/data/docs/

//sidenav.yml` in sync when adding, removing, moving, or renaming pages under `docs/content/docs/
/`. +- Honor `.agents/documentation-guidelines.md` and `.agents/documentation-tasks.md`. diff --git a/.claude/scripts/pre-pr-gate.sh b/.claude/scripts/pre-pr-gate.sh new file mode 100755 index 000000000..cb80b3125 --- /dev/null +++ b/.claude/scripts/pre-pr-gate.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# +# PreToolUse hook: block `gh pr create` unless /pre-pr has successfully run +# for the current HEAD. The hook is intentionally unaware of the repository's +# versioning or build system; the /pre-pr skill decides which checks apply. +# +# Input: hook JSON on stdin (tool_name, tool_input.command). +# Exit: 0 to allow, 2 to block (stderr is surfaced to Claude). +# +set -eu + +input=$(cat) +tool=$(printf '%s' "$input" | jq -r '.tool_name // empty') +[ "$tool" != "Bash" ] && exit 0 + +cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty') + +# Split the command on shell separators (`;`, `&`, `|` — `&&`/`||` collapse +# to repeated newlines, which is fine) and check each segment. Only block +# when a segment STARTS (after optional whitespace) with `gh pr create`. +# This avoids false positives like `echo "gh pr create"` or test fixtures +# that mention the string, while still catching `cd dir && gh pr create` +# and `cat body | gh pr create`. `tr` is used (not `sed s///`) because +# BSD `sed` on macOS does not interpret `\n` in the replacement string. +if ! printf '%s' "$cmd" \ + | tr ';&|' '\n\n\n' \ + | grep -qE '^[[:space:]]*gh[[:space:]]+pr[[:space:]]+create([[:space:]]|$)'; then + exit 0 +fi + +repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 +sentinel="$repo_root/.git/pre-pr.ok" + +block() { + cat >&2 + exit 2 +} + +if [ ! -f "$sentinel" ]; then + block <&2 <<'EOF' +Direct edits to version.gradle.kts are blocked by a project hook. + +If this repository already has a root version.gradle.kts, use the bump-version +skill instead: + /bump-version [snapshot|minor|major] + +If this repository does not have a root version.gradle.kts, do not add one just +to satisfy /pre-pr; the version check is not applicable. + +See: + - .agents/version-policy.md + - .agents/skills/bump-version/SKILL.md +EOF + exit 2 + ;; +esac + +exit 0 diff --git a/.claude/scripts/sanitize-source-code.kt b/.claude/scripts/sanitize-source-code.kt new file mode 100755 index 000000000..357d789c5 --- /dev/null +++ b/.claude/scripts/sanitize-source-code.kt @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# +# PostToolUse hook: enforce the source-code formatting rules from +# .agents/coding-guidelines.md after Edit/Write/MultiEdit: +# - strip trailing whitespace +# - replace 2+ consecutive blank lines with a single blank line +# +# Input: hook JSON on stdin (Claude Code passes tool_input.file_path). +# Exit: 0 always (post-tool-use; never block). +# +set -eu + +input=$(cat) +file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty') + +[ -z "$file" ] && exit 0 +[ ! -f "$file" ] && exit 0 + +case "$file" in + *.java|*.kt|*.kts) ;; + *) exit 0 ;; +esac + +tmp=$(mktemp) +awk ' + { sub(/[ \t]+$/, "") } + /^$/ { blank++; if (blank > 1) next; print; next } + { blank = 0; print } +' "$file" > "$tmp" && mv "$tmp" "$file" diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..21fd266d0 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,88 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": [ + "Bash(./gradlew:*)", + "Bash(./config/gradlew:*)", + "Bash(git status:*)", + "Bash(git diff:*)", + "Bash(git log:*)", + "Bash(git show:*)", + "Bash(git branch:*)", + "Bash(git switch:*)", + "Bash(git checkout:*)", + "Bash(git add:*)", + "Bash(git restore:*)", + "Bash(git stash:*)", + "Bash(git fetch:*)", + "Bash(git rev-parse:*)", + "Bash(git ls-files:*)", + "Bash(git mv:*)", + "Bash(git submodule status:*)", + "Bash(ls:*)", + "Bash(cat:*)", + "Bash(head:*)", + "Bash(tail:*)", + "Bash(wc:*)", + "Bash(find:*)", + "Bash(rg:*)", + "Bash(grep:*)", + "Bash(mkdir:*)", + "Bash(touch:*)", + "Bash(python3 .agents/skills/update-copyright/scripts/update_copyright.py:*)", + "Bash(./config/pull)", + "Bash(./config/migrate)" + ], + "deny": [ + "Bash(git push:*)", + "Bash(git reset --hard:*)", + "Bash(git clean -fdx:*)", + "Bash(rm -rf /:*)", + "Bash(rm -rf ~:*)", + "Bash(gh pr merge:*)", + "Bash(gh release create:*)" + ], + "ask": [ + "Bash(git commit:*)", + "Bash(git rebase:*)", + "Bash(git merge:*)", + "Bash(git cherry-pick:*)", + "Bash(./gradlew publish:*)", + "Bash(./gradlew uploadArtifacts:*)", + "Bash(./gradlew clean:*)" + ] + }, + "hooks": { + "PreToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/scripts/protect-version-file.sh" + } + ] + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/scripts/pre-pr-gate.sh" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/scripts/sanitize-source-code.kt" + } + ] + } + ] + } +} diff --git a/CLAUDE.md b/CLAUDE.md index 38753d02a..6d374c672 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,17 +1,61 @@ -# Project Configuration for Claude Code +# CLAUDE.md — Project Conventions -## Agent Guidelines -Please read and follow all guidelines in the project's agent documentation: +## Project Guidelines -- Start with the table of contents: `.agents/_TOC.md`. -- Follow all linked documents from the TOC. -- Apply all coding standards, formatting rules, and project conventions found in these documents. +- Quick-reference baseline: `.agents/quick-reference-card.md` — always read this first. +- For specific tasks (code review, PR prep, dependency updates, docs, etc.): prefer the matching + skill from `.agents/skills/`. +- Full standards reference: `.agents/_TOC.md` — consult when a skill + doesn't cover the needed context. -## Key Points for Claude Code -- All guidelines in the `.agents` directory apply to Claude Code interactions. -- Pay special attention to Kotlin formatting requirements (trailing newlines, detekt compliance). -- Follow project-specific conventions documented in the agent guidelines. +## Workflow Rules -## Priority -The `.agents` directory contains the authoritative project standards. -These take precedence over default behaviors. +- Use Plan mode (`EnterPlanMode`) for architecture, refactoring, multi-file changes, or lengthy documentation. +- Write the plan to `.agents/tasks/.md` before coding. See `.agents/tasks/README.md` for format and lifecycle. +- If something goes wrong — STOP and re-plan immediately. +- One focused task per subagent. + +## Memory + +Two stores, split by audience: + +- **Team-shared memory** lives in `.agents/memory/` (checked into git). Use it + for feedback rules, durable project rationale, and external system pointers. + See `.agents/memory/README.md` for layout and write protocol. +- **Per-developer memory** lives in the built-in auto-memory dir. Use it for + personal preferences, ephemeral project state, and per-machine resources. + +Litmus test: *would a teammate benefit from this next month?* → repo. +Otherwise → auto-memory. + +Review `.agents/memory/MEMORY.md` at the start of every session. +Ruthlessly iterate until mistakes stop repeating. + +## Verification & Quality + +- Never mark a task done without proof (tests, logs, diff vs main). +- Ask: "Would a senior/staff engineer approve this?" +- For non-trivial changes: pause and consider a more elegant solution. +- Fix bugs autonomously — find root cause, no hand-holding, no band-aids. + +## Core Principles + +- Simplicity first: minimal code impact, minimal surface area. +- No laziness: always find root causes. +- Minimal side effects: avoid new bugs. +- Prefer early returns and clear naming. +- Challenge your own work before presenting it. + +## Task Flow + +1. Draft plan → `.agents/tasks/.md` (see README there). +2. Show plan (`ExitPlanMode`) before implementing. +3. Execute + track progress (file at checkpoints, `TaskCreate` for live status). +4. Verify + summarize changes. +5. Update memory if lessons emerged. +6. Delete the task file on merge to master. + +## Final Rule + +This is living team memory. Update it regularly and keep it concise +(<120 lines / ~2.5k tokens). diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 4184e9bf2..56f353045 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -153,7 +153,7 @@ val junitVersion = "6.0.3" * * @see [io.spine.dependency.test.Kotest] */ -val kotestVersion = "6.1.10" +val kotestVersion = "6.1.11" configurations.all { resolutionStrategy { 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 6ee3b088e..2bdda554e 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -44,9 +44,9 @@ object CoreJvmCompiler { const val group = Spine.toolsGroup /** - * The version used to in the build classpath. + * The version used in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.064" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.063" /** * The version to be used for integration tests. 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 e6c184c9c..4e285fa9d 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.240" + override val version = "2.0.0-SNAPSHOT.238" 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 c3e302c79..600deeda7 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.421" + const val version = "2.0.0-SNAPSHOT.415" /** * The last version of Validation compatible with ProtoData. diff --git a/config b/config index c3ab20a7f..9be60eff3 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit c3ab20a7fc8cf1464ff45651a19935b5b7aeaea7 +Subproject commit 9be60eff319917082922f34b1363c55f80c11c66 From 27cee10c9ad6785c6af12ba23e42fe0ac476c9f4 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 19 May 2026 19:14:53 +0100 Subject: [PATCH 09/12] Bump local dependencies --- buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt | 4 ++-- .../src/main/kotlin/io/spine/dependency/local/Compiler.kt | 4 ++-- .../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 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt index 463cbf2b1..6a0a489cc 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt @@ -33,8 +33,8 @@ package io.spine.dependency.local */ @Suppress("ConstPropertyName", "unused") object Base { - const val version = "2.0.0-SNAPSHOT.387" - const val versionForBuildScript = "2.0.0-SNAPSHOT.387" + const val version = "2.0.0-SNAPSHOT.389" + const val versionForBuildScript = "2.0.0-SNAPSHOT.389" const val group = Spine.group private const val prefix = "spine" const val libModule = "$prefix-base" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt index 330917d7c..9f65ab246 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt @@ -72,7 +72,7 @@ object Compiler : Dependency() { * The version of the Compiler dependencies. */ override val version: String - private const val fallbackVersion = "2.0.0-SNAPSHOT.043" + private const val fallbackVersion = "2.0.0-SNAPSHOT.044" /** * The distinct version of the Compiler used by other build tools. @@ -81,7 +81,7 @@ object Compiler : Dependency() { * transitive dependencies, this is the version used to build the project itself. */ val dogfoodingVersion: String - private const val fallbackDfVersion = "2.0.0-SNAPSHOT.043" + private const val fallbackDfVersion = "2.0.0-SNAPSHOT.044" /** * The artifact for the Compiler Gradle plugin. 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 2bdda554e..3cf7c7e19 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 in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.063" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.065" /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.063" + const val version = "2.0.0-SNAPSHOT.065" /** * 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 4e285fa9d..132d7a88e 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.238" + override val version = "2.0.0-SNAPSHOT.241" 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 600deeda7..225854e77 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.415" + const val version = "2.0.0-SNAPSHOT.430" /** * The last version of Validation compatible with ProtoData. From 3747bed6c8aa31d0d815c21fd9aadc28baf8f1c9 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 19 May 2026 19:15:04 +0100 Subject: [PATCH 10/12] Bump version -> `2.0.0-SNAPSHOT.242` --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index e0a62b4cb..bd2872a06 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -27,4 +27,4 @@ /** * The version of this library for publishing. */ -val versionToPublish by extra("2.0.0-SNAPSHOT.241") +val versionToPublish by extra("2.0.0-SNAPSHOT.242") From e5cdd807f83dcd4f2020ac6e2b96584e574bc4a1 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 19 May 2026 19:20:55 +0100 Subject: [PATCH 11/12] Force Spine Enviropnment lib in tests --- tests/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index df036db08..41f468354 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -136,6 +136,7 @@ configurations { Reflect.lib, Base.annotations, Base.lib, + Base.environment, Logging.lib, Logging.middleware, Logging.testLib, From 8f3432f1337a2ddbc9c3c0d1517848cf480a5823 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 19 May 2026 19:23:24 +0100 Subject: [PATCH 12/12] Update dependency reports --- docs/dependencies/dependencies.md | 28 ++++++++++++++-------------- docs/dependencies/pom.xml | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index 1c09fac23..ea89c0951 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:time-gradle-plugin:2.0.0-SNAPSHOT.241` +# Dependencies of `io.spine.tools:time-gradle-plugin:2.0.0-SNAPSHOT.242` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1059,14 +1059,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue May 19 15:26:19 WEST 2026** using +This report was generated on **Tue May 19 19:23:05 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:time-testlib:2.0.0-SNAPSHOT.241` +# Dependencies of `io.spine.tools:time-testlib:2.0.0-SNAPSHOT.242` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -1869,14 +1869,14 @@ This report was generated on **Tue May 19 15:26:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue May 19 15:26:19 WEST 2026** using +This report was generated on **Tue May 19 19:23:05 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-time:2.0.0-SNAPSHOT.241` +# Dependencies of `io.spine:spine-time:2.0.0-SNAPSHOT.242` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -2833,14 +2833,14 @@ This report was generated on **Tue May 19 15:26:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue May 19 15:26:19 WEST 2026** using +This report was generated on **Tue May 19 19:23:05 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-time-java:2.0.0-SNAPSHOT.241` +# Dependencies of `io.spine:spine-time-java:2.0.0-SNAPSHOT.242` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -3643,14 +3643,14 @@ This report was generated on **Tue May 19 15:26:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue May 19 15:26:19 WEST 2026** using +This report was generated on **Tue May 19 19:23:05 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-time-kotlin:2.0.0-SNAPSHOT.241` +# Dependencies of `io.spine:spine-time-kotlin:2.0.0-SNAPSHOT.242` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -4461,14 +4461,14 @@ This report was generated on **Tue May 19 15:26:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue May 19 15:26:19 WEST 2026** using +This report was generated on **Tue May 19 19:23:05 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:time-validation:2.0.0-SNAPSHOT.241` +# Dependencies of `io.spine.tools:time-validation:2.0.0-SNAPSHOT.242` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -5590,14 +5590,14 @@ This report was generated on **Tue May 19 15:26:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue May 19 15:26:19 WEST 2026** using +This report was generated on **Tue May 19 19:23:05 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-tests:2.0.0-SNAPSHOT.241` +# Dependencies of `io.spine:spine-validation-tests:2.0.0-SNAPSHOT.242` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -6683,6 +6683,6 @@ This report was generated on **Tue May 19 15:26:19 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue May 19 15:26:19 WEST 2026** using +This report was generated on **Tue May 19 19:23:05 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/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index 3018403a5..026ab31bd 100644 --- a/docs/dependencies/pom.xml +++ b/docs/dependencies/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine spine-time -2.0.0-SNAPSHOT.241 +2.0.0-SNAPSHOT.242 2015 @@ -56,7 +56,7 @@ all modules and does not describe the project structure per-subproject. io.spine spine-time - 2.0.0-SNAPSHOT.238 + 2.0.0-SNAPSHOT.241 compile @@ -68,13 +68,13 @@ all modules and does not describe the project structure per-subproject. io.spine.tools compiler-gradle-api - 2.0.0-SNAPSHOT.043 + 2.0.0-SNAPSHOT.044 compile io.spine.tools compiler-jvm - 2.0.0-SNAPSHOT.043 + 2.0.0-SNAPSHOT.044 compile @@ -140,7 +140,7 @@ all modules and does not describe the project structure per-subproject. io.spine.tools compiler-testlib - 2.0.0-SNAPSHOT.043 + 2.0.0-SNAPSHOT.044 test @@ -235,12 +235,12 @@ all modules and does not describe the project structure per-subproject. io.spine.tools compiler-cli-all - 2.0.0-SNAPSHOT.043 + 2.0.0-SNAPSHOT.044 io.spine.tools compiler-protoc-plugin - 2.0.0-SNAPSHOT.043 + 2.0.0-SNAPSHOT.044 io.spine.tools