Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .agents/tasks/de-event-sourcing-rollout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Adopt the de-event-sourcing storage API of core-jvm (Phase H rollout)

Upstream plan: `core-jvm/.agents/tasks/de-event-sourcing-plan.md`, Phase H.
Branch: `de-event-sourcing`. Depends on core-jvm `2.0.0-SNAPSHOT.522`.

## Problem

core-jvm removed event-sourced aggregate loading. For storage vendors:

- `AggregateStorage`, `StorageFactory.createAggregateStorage`, and the published
`AggregateStorageTest` fixture are **removed**. Aggregate latest state arrives
via `createEntityRecordStorage`/`createRecordStorage` with `group == null`.
- `createRecordStorage` gained a `@Nullable StorageGroup group` parameter.
Non-null groups arrive from the per-entity histories (`EntityEventStorage`,
`EntityStateHistoryStorage`), both named after the entity state type.
The vendor must allocate physical storage by the **(group, recordType)** pair.
- `createEntityStateHistoryStorage` may be invoked concurrently (delivery
worker threads); the factory must tolerate that.

Without honoring the group, JDBC table identity (`RecordSpec.sourceType()` alone)
conflates: all event journals with each other **and** with the event log
(`sourceType == Event` everywhere), and an entity's state history with its
latest-state storage — with `TableSpecs` handing out a cached spec with
the wrong ID column type.

## Fix

Mechanical half (earlier commits on the branch): 3-arg `createRecordStorage`
signature, `RecordStorageDelegateTest` → `DelegatingRecordStorageTest` renames,
obsolete aggregate test suites deleted, version bumped (`.110`).

Substantive half (this change):

- `TableNames.of(recordType, group)`: grouped table name =
sanitized group name + `_` + record type simple name
(e.g. `spine_test_storage_StgProject_Event`). Naming settled with
the product owner on 2026-08-04 (generic rule over semantic suffixes).
- `TableSpecs`: cache keyed by `SpecKey(sourceType, recordType, groupName)`
in a `ConcurrentHashMap` (`computeIfAbsent`). Custom names and mappings are
looked up by the *source* type (Codex review of #181: for an entity, that is
the state type users register — `recordType()` is `EntityRecord`; the miss
predates the branch, present since CoreJvm `.380`). Grouped tables take
custom names via the `setTableName(stateType, recordType, name)` overload,
keyed by `(group name, record type)`; the single-type names never apply to
them. Custom mappings (source-type-keyed) serve grouped tables too.
- `JdbcStorageFactory.createRecordStorage` threads the group into
`JdbcRecordStorage`; new `tableSpecFor(spec, group)` overload.
- `JdbcRecordStorage`: group-accepting constructors; legacy ones delegate
with `group = null` (`JdbcSessionStorage`, `tableCreationSql` unchanged).
- Deleted the dead `io.spine.server.storage.jdbc.aggregate` package
(dangling `{@link AggregateStorage}` broke Dokka).
- `docs/tables.md`: "Grouped tables" section.

New Kotlin specs (H2): `GroupedTableAllocationSpec` (the vendor allocation
contract), `JdbcEntityEventStorageSpec`, `JdbcEntityStateHistoryStorageSpec`
(round-trips incl. `EntityStateKey` Message ID, upsert overwrite, `stateAt`,
`trim`, `truncate`), `ConcurrentHistoryCreationSpec`.

## Follow-ups (out of scope)

- Grouped-DDL export via `tableCreationSql` (currently latest-state only).

## Status

Implemented; `./gradlew build dokkaGenerate` green. Delete this file on merge
to master.
6 changes: 5 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"plansDirectory": ".claude/plans",
"permissions": {
"allow": [
"Edit(version.gradle.kts)",
Expand Down Expand Up @@ -32,8 +33,11 @@
"Bash(mkdir:*)",
"Bash(touch:*)",
"Bash(python3 .agents/skills/update-copyright/scripts/update_copyright.py:*)",
"Bash(.agents/skills/version-bumped/scripts/version-bumped.sh)",
"Bash(./config/pull)",
"Bash(./config/migrate)"
"Bash(./config/migrate)",
"Skill(pre-pr)",
"Skill(pre-pr:*)"
],
"deny": [
"Bash(git reset --hard:*)",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-on-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
# there anyway).
- name: Upload code coverage report
if: steps.codecov.outputs.available == 'true' || github.event_name == 'push'
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
Expand Down
13 changes: 11 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@
.idea/modules
.idea/shelf

# `.idea/misc.xml` is intentionally NOT re-included below. It is project-local —
# it holds the per-project JDK name and IDEA's own churn (entry-point list
# indices, external-storage toggles) — so `.idea/*.xml` above keeps it ignored.
# `./config/pull` (via `migrate`) untracks any copy an earlier pull committed.

# Do not ignore the following IDEA settings
!.idea/misc.xml
!.idea/codeStyleSettings.xml
!.idea/codeStyles/
!.idea/copyright/
Expand Down Expand Up @@ -173,6 +177,12 @@ __pycache__/

# Claude working files
/.claude/worktrees/
# Ephemeral plan-mode scratch (durable task docs live in `.agents/tasks/`).
/.claude/plans/

# Personal, per-developer Claude Code settings overrides (never committed;
# the distributed `.claude/settings.json` is the shared, committed layer).
/.claude/settings.local.json

# Auto-downloaded Lychee binary used by the `check-links` skill.
/.agents/skills/check-links/.cache/
Expand All @@ -192,7 +202,6 @@ docs/_preview/resources/
# >>> repo-local entries (preserved across ./config/pull) >>>
# Copyright 2023, TeamDev. All rights reserved.
# http://www.apache.org/licenses/LICENSE-2.0
!.idea/misc.xml
!.idea/codeStyleSettings.xml
!.idea/codeStyles/
!.idea/copyright/
Expand Down
2 changes: 1 addition & 1 deletion .idea/live-templates/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 0 additions & 44 deletions .idea/misc.xml

This file was deleted.

4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ Code contributions should:
Contributor License Agreement
-----------------------------
Contributions to the code of Spine Event Engine framework and its libraries must be accompanied by
Contributor License Agreement (CLA).
a Contributor License Agreement (CLA).

* If you are an individual writing original source code and you're sure you own
the intellectual property, then you'll need to sign an individual CLA.

* If you work for a company which wants you to contribute your work,
* If you work for a company that wants you to contribute your work,
then an authorized person from your company will need to sign a corporate CLA.

Please [contact us][legal-email] for arranging the paper formalities.
Expand Down
6 changes: 3 additions & 3 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ val kotestJvmPluginVersion = "0.4.10"
/**
* @see [io.spine.dependency.test.Kover]
*/
val koverVersion = "0.9.8"
val koverVersion = "0.9.9"

/**
* The version of the Shadow Plugin.
Expand Down Expand Up @@ -222,7 +222,7 @@ dependOnBuildSrcJar()
/**
* Adds a dependency on a `buildSrc.jar`, iff:
* 1) the `src` folder is missing, and
* 2) `buildSrc.jar` is present in `buildSrc/` folder instead.
* 2) `buildSrc.jar` is present in the `buildSrc/` folder instead.
*
* This approach is used in the scope of integration testing.
*/
Expand All @@ -241,7 +241,7 @@ fun Project.dependOnBuildSrcJar() {
* Includes the `implementation` dependency on `artifactregistry-auth-common`,
* with the version defined in [googleAuthToolVersion].
*
* `artifactregistry-auth-common` has transitive dependency on Gson and Apache `commons-codec`.
* `artifactregistry-auth-common` has a transitive dependency on Gson and Apache `commons-codec`.
* Gson from version `2.8.6` until `2.8.9` is vulnerable to Deserialization of Untrusted Data
* (https://devhub.checkmarx.com/cve-details/CVE-2022-25647/).
*
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/quality/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>

<!--
~ 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.
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/quality/checkstyle.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>

<!--
~ 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.
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
43 changes: 33 additions & 10 deletions buildSrc/src/main/kotlin/BuildExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import io.spine.dependency.build.ErrorProne
import io.spine.dependency.build.GradleDoctor
import io.spine.dependency.build.Ksp
import io.spine.dependency.build.PluginPublishPlugin
import io.spine.dependency.lib.JetBrainsAnnotations
import io.spine.dependency.lib.Protobuf
import io.spine.dependency.local.Compiler
import io.spine.dependency.local.CoreJvmCompiler
Expand All @@ -39,18 +40,20 @@ import io.spine.dependency.test.Kover
import io.spine.gradle.repo.standardToSpineSdk
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.tasks.JavaExec
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.ScriptHandlerScope
import org.gradle.kotlin.dsl.exclude
import org.gradle.plugin.use.PluginDependenciesSpec
import org.gradle.plugin.use.PluginDependencySpec

/**
* Provides shortcuts to reference our dependency objects.
*
* Dependency objects cannot be used under `plugins` section because `io` is a value
* declared in auto-generated `org.gradle.kotlin.dsl.PluginAccessors.kt` file.
* Dependency objects cannot be used under the `plugins` section because `io` is a value
* declared in the auto-generated `org.gradle.kotlin.dsl.PluginAccessors.kt` file.
* It conflicts with our own declarations.
*
* In such cases, a shortcut to apply a plugin can be created:
Expand Down Expand Up @@ -91,7 +94,7 @@ val ScriptHandlerScope.coreJvmCompiler: CoreJvmCompiler
* Shortcut to [CoreJvmCompiler] dependency object.
*
* This plugin is not published to Gradle Portal and cannot be applied directly to a project.
* Firstly, it should be put to buildscript's classpath and then applied by ID only.
* Firstly, it should be put to the buildscript's classpath and then applied by ID only.
*/
val PluginDependenciesSpec.coreJvmCompiler: CoreJvmCompiler
get() = CoreJvmCompiler
Expand All @@ -116,8 +119,8 @@ val PluginDependenciesSpec.spineCompiler: Compiler
/**
* Provides shortcuts for applying plugins from our dependency objects.
*
* Dependency objects cannot be used under `plugins` section because `io` is a value
* declared in auto-generated `org.gradle.kotlin.dsl.PluginAccessors.kt` file.
* Dependency objects cannot be used under the `plugins` section because `io` is a value
* declared in the auto-generated `org.gradle.kotlin.dsl.PluginAccessors.kt` file.
* It conflicts with our own declarations.
*
* Declaring of top-level shortcuts eliminates the need to apply plugins
Expand Down Expand Up @@ -177,10 +180,10 @@ fun Project.configureTaskDependencies() {
* Creates a dependency between the Gradle task of *this* name
* onto the task with `taskName`.
*
* If either of tasks does not exist in the enclosing `Project`,
* If either of the tasks does not exist in the enclosing `Project`,
* this method does nothing.
*
* This extension is kept local to `configureTaskDependencies` extension
* This extension is kept local to the `configureTaskDependencies` extension
* to prevent its direct usage from outside.
*/
fun String.dependOn(taskName: String) {
Expand Down Expand Up @@ -278,7 +281,7 @@ fun JavaExec.remoteDebug(enabled: Boolean = true) {
*
* @param enabled If `true` the task will be suspended.
* @throws IllegalStateException if the task with the given name is not found, or,
* if the taks is not of [JavaExec] type.
* if the task is not of [JavaExec] type.
*/
fun Project.setRemoteDebug(taskName: String, enabled: Boolean = true) {
val task = tasks.findByName(taskName)
Expand Down Expand Up @@ -324,7 +327,7 @@ fun Project.testFixturesSpineCompilerRemoteDebug(enabled: Boolean = true) =
/**
* Parts of names of configurations to be excluded by
* `artifactMeta/excludeConfigurations/containing` in the modules
* where `io.spine.atifact-meta` plugin is applied.
* where the `io.spine.atifact-meta` plugin is applied.
*/
val buildToolConfigurations: Array<String> = arrayOf(
"detekt",
Expand All @@ -337,7 +340,7 @@ val buildToolConfigurations: Array<String> = arrayOf(
)

/**
* Make the `sourcesJar` task accept duplicated input, which seems to occur
* Makes the `sourcesJar` task accept duplicated input, which seems to occur
* somewhere inside Protobuf Gradle Plugin.
*/
fun Project.allowDuplicationInSourcesJar() {
Expand All @@ -347,3 +350,23 @@ fun Project.allowDuplicationInSourcesJar() {
}
}
}

/**
* Excludes `org.jetbrains:annotations` from this published dependency.
*
* Build script classpaths pin the module to the version used by the Kotlin
* runtime embedded into Gradle (`strictly 13.0`, "Pinned to the embedded
* Kotlin"), while `kotlinx-coroutines` and other transitive dependencies require
* later versions such as `23.0.0`.
* Gradle 9.6 may fail to reconcile the two declarations — the outcome depends on
* the shape of the consumer's dependency graph — making the plugin unresolvable
* without a consumer-side workaround, such as forcing the module version on
* the build script classpath.
*
* The annotations are compile-time metadata, not needed at runtime.
* Consumers still receive version `13.0` through the `kotlin-stdlib`
* dependency, which satisfies the pin.
*/
fun ModuleDependency.excludeJetBrainsAnnotations() {
exclude(group = JetBrainsAnnotations.groupId, module = JetBrainsAnnotations.artifactId)
}
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/BuildSettings.kt
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Loading
Loading