diff --git a/.agents/tasks/archive/cascade-refresh-604420c.json b/.agents/tasks/archive/cascade-refresh-604420c.json new file mode 100644 index 0000000..574aa4d --- /dev/null +++ b/.agents/tasks/archive/cascade-refresh-604420c.json @@ -0,0 +1,649 @@ +{ + "schema": 1, + "wave": "cascade-refresh-604420c", + "kind": "refresh", + "created": "2026-10-21T15:25:26Z", + "base": "master", + "config_sha": "604420cd7eab82b382c9a5c1d812be454552c27c", + "seed": null, + "target": null, + "branch": "cascade-refresh-604420c", + "halted": null, + "order": [ + "base-libraries", + "reflect", + "logging", + "testlib", + "dokka-tools", + "tool-base", + "ProtoTap", + "compiler", + "validation", + "core-jvm-compiler", + "base-types", + "change", + "time", + "money", + "core-jvm", + "jdbc-storage", + "gcloud-jvm", + "delivery", + "BuildSpeed" + ], + "repos": { + "base-libraries": { + "upstreams": [], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-base" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.462", + "pin_vector": {}, + "adapt_rounds": 0, + "built": { + "head": "e6ce655acb5683b3e475deb7bed0d8971195bea3", + "at": "2026-10-21T15:43:07Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/base-libraries/pull/968", + "head": "e6ce655acb5683b3e475deb7bed0d8971195bea3" + }, + "parked": null + }, + "reflect": { + "upstreams": [], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-reflect" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.203", + "pin_vector": {}, + "adapt_rounds": 0, + "built": { + "head": "867d9cf05a0224a6c9dc3768785ae9a82d56458b", + "at": "2026-10-21T15:43:26Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/reflect/pull/16", + "head": "867d9cf05a0224a6c9dc3768785ae9a82d56458b" + }, + "parked": null + }, + "logging": { + "upstreams": [ + "base-libraries", + "reflect" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-logging" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.428", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "reflect": "2.0.0-SNAPSHOT.203" + }, + "adapt_rounds": 1, + "built": { + "head": "ecacc07a6bd1130faa5b5302f6deecd0469d9760", + "at": "2026-10-21T15:55:46Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/logging/pull/153", + "head": "9f75c7f2c5e304ea358c4304caf4d495ac20f81a" + }, + "parked": null + }, + "testlib": { + "upstreams": [ + "logging" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "base-testlib" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.216", + "pin_vector": { + "logging": "2.0.0-SNAPSHOT.427" + }, + "adapt_rounds": 0, + "built": { + "head": "b8018f38dc4cbdbe1322c91f97652ef535d779d1", + "at": "2026-10-21T15:56:18Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/testlib/pull/16", + "head": "b8018f38dc4cbdbe1322c91f97652ef535d779d1" + }, + "parked": null + }, + "dokka-tools": { + "upstreams": [ + "base-libraries" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "dokka-extensions" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.12", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462" + }, + "adapt_rounds": 0, + "built": { + "head": "bae4036811749421871d065a0bf842b6bd249749", + "at": "2026-10-21T15:56:40Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/dokka-tools/pull/13", + "head": "bae4036811749421871d065a0bf842b6bd249749" + }, + "parked": null + }, + "tool-base": { + "upstreams": [ + "base-libraries", + "logging", + "testlib" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "jvm-tools" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.428", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "logging": "2.0.0-SNAPSHOT.427", + "testlib": "2.0.0-SNAPSHOT.216" + }, + "adapt_rounds": 0, + "built": { + "head": "513bc9e6e8e3d77f027eaf75e031bd987acfc9d2", + "at": "2026-10-21T15:59:24Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/tool-base/pull/197", + "head": "513bc9e6e8e3d77f027eaf75e031bd987acfc9d2" + }, + "parked": null + }, + "ProtoTap": { + "upstreams": [ + "base-libraries", + "logging", + "testlib", + "tool-base" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "prototap-gradle-plugin" + }, + "state": "published-remote", + "target_version": "0.17.4", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "logging": "2.0.0-SNAPSHOT.428", + "testlib": "2.0.0-SNAPSHOT.216", + "tool-base": "2.0.0-SNAPSHOT.428" + }, + "adapt_rounds": 0, + "built": { + "head": "c1a526d3e9d6e97b633b779b0f019e9ba223d3d2", + "at": "2026-10-21T19:42:33Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/ProtoTap/pull/24", + "head": "08b843a3068eedaf80ba7912c1d2b529df7a45ac" + }, + "parked": null + }, + "compiler": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "logging", + "reflect", + "testlib", + "tool-base" + ], + "class": "library", + "version_property": "compilerVersion", + "probe": { + "group_path": "io/spine/tools", + "artifact": "compiler-jvm" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.081", + "pin_vector": { + "ProtoTap": "0.17.4", + "base-libraries": "2.0.0-SNAPSHOT.462", + "logging": "2.0.0-SNAPSHOT.428", + "reflect": "2.0.0-SNAPSHOT.203", + "testlib": "2.0.0-SNAPSHOT.216", + "tool-base": "2.0.0-SNAPSHOT.428" + }, + "adapt_rounds": 0, + "built": { + "head": "b5d8b3d69301fd527f35080b165e22e42e68c18d", + "at": "2026-10-22T14:16:20Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/compiler/pull/97", + "head": "b5d8b3d69301fd527f35080b165e22e42e68c18d" + }, + "parked": null + }, + "validation": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "compiler", + "logging", + "reflect", + "testlib", + "tool-base" + ], + "class": "library", + "version_property": "validationVersion", + "probe": { + "group_path": "io/spine/tools", + "artifact": "validation-java" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.469", + "pin_vector": { + "ProtoTap": "0.17.4", + "base-libraries": "2.0.0-SNAPSHOT.462", + "compiler": "2.0.0-SNAPSHOT.081", + "logging": "2.0.0-SNAPSHOT.428", + "reflect": "2.0.0-SNAPSHOT.203", + "testlib": "2.0.0-SNAPSHOT.216", + "tool-base": "2.0.0-SNAPSHOT.428" + }, + "adapt_rounds": 0, + "built": { + "head": "c9855b75bf1f382c1c27f91afe9facdcb8803a48", + "at": "2026-10-22T14:18:38Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/validation/pull/338", + "head": "c9855b75bf1f382c1c27f91afe9facdcb8803a48" + }, + "parked": null + }, + "core-jvm-compiler": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "compiler", + "logging", + "reflect", + "testlib", + "tool-base", + "validation" + ], + "class": "library", + "version_property": "coreJvmCompilerVersion", + "probe": { + "group_path": "io/spine/tools", + "artifact": "core-jvm-gradle-plugin" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.099", + "pin_vector": { + "ProtoTap": "0.17.4", + "base-libraries": "2.0.0-SNAPSHOT.462", + "compiler": "2.0.0-SNAPSHOT.081", + "logging": "2.0.0-SNAPSHOT.428", + "reflect": "2.0.0-SNAPSHOT.203", + "testlib": "2.0.0-SNAPSHOT.216", + "tool-base": "2.0.0-SNAPSHOT.428", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "b27f8ab9e26fa4069239482781fa94ad8996a264", + "at": "2026-10-22T14:22:58Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/core-jvm-compiler/pull/121", + "head": "b27f8ab9e26fa4069239482781fa94ad8996a264" + }, + "parked": null + }, + "base-types": { + "upstreams": [ + "base-libraries", + "compiler", + "core-jvm-compiler", + "testlib", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-base-types" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.227", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "testlib": "2.0.0-SNAPSHOT.216", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "e3078e44252217f0591e2dc538179389ac57b3da", + "at": "2026-10-22T14:23:35Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/base-types/pull/57", + "head": "e3078e44252217f0591e2dc538179389ac57b3da" + }, + "parked": null + }, + "change": { + "upstreams": [ + "base-libraries", + "compiler", + "core-jvm-compiler", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-change" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.209", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "bc59e6ed9ee2b39459472e6882fc09d35212fce6", + "at": "2026-10-22T14:24:06Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/change/pull/10", + "head": "bc59e6ed9ee2b39459472e6882fc09d35212fce6" + }, + "parked": null + }, + "time": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "compiler", + "core-jvm-compiler", + "logging", + "testlib", + "tool-base", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-time" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.253", + "pin_vector": { + "ProtoTap": "0.17.4", + "base-libraries": "2.0.0-SNAPSHOT.462", + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "logging": "2.0.0-SNAPSHOT.428", + "testlib": "2.0.0-SNAPSHOT.216", + "tool-base": "2.0.0-SNAPSHOT.428", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 1, + "built": { + "head": "ba06ebe4d758bf84de64e14660304d64a3ab6a6c", + "at": "2026-10-22T18:52:59Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/time/pull/202", + "head": "ba06ebe4d758bf84de64e14660304d64a3ab6a6c" + }, + "parked": null + }, + "money": { + "upstreams": [ + "base-libraries", + "compiler", + "core-jvm-compiler", + "testlib", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-money" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.13", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "testlib": "2.0.0-SNAPSHOT.216", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "88e65d7661352fe9ab7ae284230f511ff98a28ab", + "at": "2026-10-22T14:26:01Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/money/pull/33", + "head": "88e65d7661352fe9ab7ae284230f511ff98a28ab" + }, + "parked": null + }, + "core-jvm": { + "upstreams": [ + "base-libraries", + "base-types", + "change", + "compiler", + "core-jvm-compiler", + "logging", + "reflect", + "testlib", + "time", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-core" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.577", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "base-types": "2.0.0-SNAPSHOT.227", + "change": "2.0.0-SNAPSHOT.209", + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "logging": "2.0.0-SNAPSHOT.428", + "reflect": "2.0.0-SNAPSHOT.203", + "testlib": "2.0.0-SNAPSHOT.216", + "time": "2.0.0-SNAPSHOT.253", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "8e7ff830d518fa74e67ece6ccb53cee6ccd8af38", + "at": "2026-10-22T20:24:42Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/core-jvm/pull/1697", + "head": "ee9be9d3fa7a1bccea910243e3becc8da2099f32" + }, + "parked": null + }, + "jdbc-storage": { + "upstreams": [ + "compiler", + "core-jvm", + "core-jvm-compiler", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-rdbms" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.131", + "pin_vector": { + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm": "2.0.0-SNAPSHOT.577", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "0e1e71f937700f9c49946517de7e817d48d0a604", + "at": "2026-10-22T20:25:52Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/jdbc-storage/pull/186", + "head": "0e1e71f937700f9c49946517de7e817d48d0a604" + }, + "parked": null + }, + "gcloud-jvm": { + "upstreams": [ + "base-libraries", + "base-types", + "compiler", + "core-jvm", + "core-jvm-compiler", + "logging", + "testlib", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/gcloud", + "artifact": "spine-datastore" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.241", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.462", + "base-types": "2.0.0-SNAPSHOT.227", + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm": "2.0.0-SNAPSHOT.577", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "logging": "2.0.0-SNAPSHOT.428", + "testlib": "2.0.0-SNAPSHOT.216", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "ddb61bfe239c56f0bd8d5fabf869104ce19dfff9", + "at": "2026-10-22T20:28:03Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/gcloud-jvm/pull/210", + "head": "ddb61bfe239c56f0bd8d5fabf869104ce19dfff9" + }, + "parked": null + }, + "delivery": { + "upstreams": [ + "compiler", + "core-jvm", + "core-jvm-compiler", + "logging", + "testlib", + "time", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/delivery", + "artifact": "spine-delivery-server" + }, + "state": "published-remote", + "target_version": "0.24.1", + "pin_vector": { + "compiler": "2.0.0-SNAPSHOT.081", + "core-jvm": "2.0.0-SNAPSHOT.577", + "core-jvm-compiler": "2.0.0-SNAPSHOT.099", + "logging": "2.0.0-SNAPSHOT.428", + "testlib": "2.0.0-SNAPSHOT.216", + "time": "2.0.0-SNAPSHOT.253", + "validation": "2.0.0-SNAPSHOT.469" + }, + "adapt_rounds": 0, + "built": { + "head": "75bb56c07fe6c354816a9762ee504d1c31f7e535", + "at": "2026-10-22T20:48:29Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/delivery/pull/85", + "head": "75bb56c07fe6c354816a9762ee504d1c31f7e535" + }, + "parked": null + }, + "BuildSpeed": { + "upstreams": [ + "core-jvm-compiler" + ], + "class": "verify-only", + "version_property": "", + "probe": { + "group_path": "", + "artifact": "" + }, + "state": "parked", + "target_version": null, + "pin_vector": {}, + "adapt_rounds": 0, + "built": null, + "pr": null, + "parked": "verify-only: SDK versions arrive as CI environment variables substituted into settings.gradle.kts.template, so there are no file pins to apply, no version to bump, and no artifact to publish" + } + }, + "excluded": {}, + "closing_pr": "https://github.com/SpineEventEngine/config/pull/818" +} diff --git a/.agents/tasks/archive/cascade-refresh-604420c.md b/.agents/tasks/archive/cascade-refresh-604420c.md new file mode 100644 index 0000000..4d27f0e --- /dev/null +++ b/.agents/tasks/archive/cascade-refresh-604420c.md @@ -0,0 +1,49 @@ +--- +slug: cascade-refresh-604420c +branch: cascade-refresh-604420c +owner: cascade +status: in-progress +started: 2026-10-21 +--- + +# Wave `cascade-refresh-604420c` (refresh) + +Machine state: [`cascade-refresh-604420c.json`](cascade-refresh-604420c.json). Park reasons and decisions land here. + +## Repos + +- [ ] base-libraries +- [ ] reflect +- [ ] logging +- [ ] testlib +- [ ] dokka-tools +- [ ] tool-base +- [ ] ProtoTap +- [ ] compiler +- [ ] validation +- [ ] core-jvm-compiler +- [ ] base-types +- [ ] change +- [ ] time +- [ ] money +- [ ] core-jvm +- [ ] jdbc-storage +- [ ] gcloud-jvm +- [ ] delivery +- [ ] BuildSpeed + +## Log + +- 2026-10-21 — wave planned. +- 2026-10-21 — **parked** `BuildSpeed`: verify-only: SDK versions arrive as CI environment variables substituted into settings.gradle.kts.template, so there are no file pins to apply, no version to bump, and no artifact to publish +- 2026-10-21 — **parked** `core-jvm-compiler`: CoreJvmPluginIgTest: applying the published plugin by ID in plugins{} with Compiler .080 on the buildscript classpath fails on Gradle's embedded-Kotlin pin (org.jetbrains:annotations strictly 13.0); worked with Compiler .074 +- 2026-10-21 — resumed `core-jvm-compiler`. +- 2026-10-21 — `core-jvm-compiler` unparked. Root cause of the `CoreJvmPluginIgTest` failure: stale local builds from the abandoned `a0cbbbf` wave in `~/.m2` (core-jvm `.571`/`.572`, base-types `.226`, built with Kotlin 2.4.21 and endorsing `kotlin-bom` 2.4.21) shadowed the registry artifacts of the same versions, because the TestKit project lists `mavenLocal()` first; their graph cannot be reconciled with Gradle's strict embedded-Kotlin pins (`kotlin-stdlib` 2.4.10, `annotations` 13.0). Moved them to the session scratchpad; CI never sees them. +- 2026-10-21 — follow-up after the wave: config#813 (merged as `a53eca4b2e`) declares `OpenTelemetryKotlin.exportersCore`/`exportersOtlp`; once `logging` pulls a config at or past it, `otel-backend-bootstrap/build.gradle.kts` can drop the inline coordinates from `0bf9a733`. +- 2026-10-21 — retargeted `logging` to `2.0.0-SNAPSHOT.428` (logging#153, on the latest `config` with the named OpenTelemetry exporter accessors) at the owner's request; the wave branch was fast-forwarded to master. Unshipped downstream repos re-pin to `.428`; the already-published testlib `.216` and tool-base `.428` keep `.427` as a floor. +- 2026-10-21 — ProtoTap#24 merged on its pre-re-pin head (`08b843a`), so ProtoTap `0.17.4` pins logging `.427` (a floor, like testlib and tool-base); its local re-pin commits stay unpushed, and the local rebuild of `0.17.4` was moved out of `~/.m2`. +- 2026-10-22 — `compiler` `.080` published partly (GitHub Packages answered 409 Conflict on an already uploaded checksum). Re-shipped as `.081` (compiler#97) on `config` `ad3dff08`, the hardened publishing of config#814; the ten repos without PRs pulled the same `config` and re-pinned to `.081`. +- 2026-10-22 — four PRs shipped together hit the Artifact Registry's HTTP 429 rate limit in CI; the driver now re-runs jobs that failed on 429 only. +- 2026-10-22 — the push event of the time#201 merge arrived 17 minutes late, so `.252` did publish; `time` was re-shipped anyway as `.253` (time#202) to try `config` `58cd2d4a` (config#815, `publishApiDocs`), whose rename broke consumers' own Gradle scripts until config#817 taught `migrate` to rewrite them. `core-jvm` and `delivery` re-pinned to `.253`. +- 2026-10-23 — core-jvm#1697 merged master (`.576`, from core-jvm#1696) and kept `.577`; `core-jvm`, `jdbc-storage`, `gcloud-jvm` and `delivery` shipped on `config` `c5d6e5b2`, whose `migrate` rewrote their scripts. +- 2026-10-23 — **closed**: config#818 (merged as `fcd68830`) records the wave's version vector. diff --git a/.agents/tasks/archive/cascade-refresh-a0cbbbf.json b/.agents/tasks/archive/cascade-refresh-a0cbbbf.json new file mode 100644 index 0000000..a85fedb --- /dev/null +++ b/.agents/tasks/archive/cascade-refresh-a0cbbbf.json @@ -0,0 +1,621 @@ +{ + "schema": 1, + "wave": "cascade-refresh-a0cbbbf", + "kind": "refresh", + "created": "2026-10-15T19:00:56Z", + "base": "master", + "config_sha": "a0cbbbfe66c12475224563439fb6deb3da71ba58", + "seed": null, + "target": null, + "branch": "cascade-refresh-a0cbbbf", + "halted": null, + "order": [ + "base-libraries", + "reflect", + "logging", + "testlib", + "dokka-tools", + "tool-base", + "ProtoTap", + "compiler", + "validation", + "core-jvm-compiler", + "base-types", + "change", + "time", + "money", + "core-jvm", + "jdbc-storage", + "gcloud-jvm", + "delivery", + "BuildSpeed" + ], + "repos": { + "base-libraries": { + "upstreams": [], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-base" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.461", + "pin_vector": {}, + "adapt_rounds": 1, + "built": { + "head": "1634e4c5eb47649831b0666717ec81610f77c9f6", + "at": "2026-10-15T19:09:34Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/base-libraries/pull/967", + "head": "1634e4c5eb47649831b0666717ec81610f77c9f6" + }, + "parked": null + }, + "reflect": { + "upstreams": [], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-reflect" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.202", + "pin_vector": {}, + "adapt_rounds": 0, + "built": { + "head": "23d55db7a207c0df47ae5abf584d7cd2a89e75e8", + "at": "2026-10-15T19:10:08Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/reflect/pull/15", + "head": "23d55db7a207c0df47ae5abf584d7cd2a89e75e8" + }, + "parked": null + }, + "logging": { + "upstreams": [ + "base-libraries", + "reflect" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-logging" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.426", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "reflect": "2.0.0-SNAPSHOT.202" + }, + "adapt_rounds": 0, + "built": { + "head": "79766b3c73d9c11ed13b1081a084d87bc21b12a7", + "at": "2026-10-15T19:11:03Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/logging/pull/151", + "head": "79766b3c73d9c11ed13b1081a084d87bc21b12a7" + }, + "parked": null + }, + "testlib": { + "upstreams": [ + "logging" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "base-testlib" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.215", + "pin_vector": { + "logging": "2.0.0-SNAPSHOT.426" + }, + "adapt_rounds": 0, + "built": { + "head": "835b21d4587c265b49abb0f63ae4552f75175215", + "at": "2026-10-15T19:11:35Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/testlib/pull/15", + "head": "835b21d4587c265b49abb0f63ae4552f75175215" + }, + "parked": null + }, + "dokka-tools": { + "upstreams": [ + "base-libraries" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "dokka-extensions" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.11", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461" + }, + "adapt_rounds": 0, + "built": { + "head": "880762b8ff3472c17e02d3441c5d239d30da8825", + "at": "2026-10-15T19:12:18Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/dokka-tools/pull/12", + "head": "880762b8ff3472c17e02d3441c5d239d30da8825" + }, + "parked": null + }, + "tool-base": { + "upstreams": [ + "base-libraries", + "logging", + "testlib" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "jvm-tools" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.426", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "logging": "2.0.0-SNAPSHOT.426", + "testlib": "2.0.0-SNAPSHOT.215" + }, + "adapt_rounds": 0, + "built": { + "head": "b1071b8b25e87619031f011350887bd949c0875a", + "at": "2026-10-15T19:14:48Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/tool-base/pull/195", + "head": "b1071b8b25e87619031f011350887bd949c0875a" + }, + "parked": null + }, + "ProtoTap": { + "upstreams": [ + "base-libraries", + "logging", + "testlib", + "tool-base" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/tools", + "artifact": "prototap-gradle-plugin" + }, + "state": "published-remote", + "target_version": "0.17.3", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "logging": "2.0.0-SNAPSHOT.426", + "testlib": "2.0.0-SNAPSHOT.215", + "tool-base": "2.0.0-SNAPSHOT.426" + }, + "adapt_rounds": 1, + "built": { + "head": "91eaaaad39c33ec1b0e6c61dcaf7a036a261c01d", + "at": "2026-10-15T19:21:02Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/ProtoTap/pull/23", + "head": "91eaaaad39c33ec1b0e6c61dcaf7a036a261c01d" + }, + "parked": null + }, + "compiler": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "logging", + "reflect", + "testlib", + "tool-base" + ], + "class": "library", + "version_property": "compilerVersion", + "probe": { + "group_path": "io/spine/tools", + "artifact": "compiler-jvm" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.074", + "pin_vector": { + "ProtoTap": "0.17.3", + "base-libraries": "2.0.0-SNAPSHOT.461", + "logging": "2.0.0-SNAPSHOT.426", + "reflect": "2.0.0-SNAPSHOT.202", + "testlib": "2.0.0-SNAPSHOT.215", + "tool-base": "2.0.0-SNAPSHOT.426" + }, + "adapt_rounds": 1, + "built": { + "head": "0ebf947332a4521f15c5c232223529cf2f8fb805", + "at": "2026-10-16T16:42:26Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/compiler/pull/92", + "head": "0ec7e51fc6f0cc8f6a83faee53672a16722021d2" + }, + "parked": null + }, + "validation": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "compiler", + "logging", + "reflect", + "testlib", + "tool-base" + ], + "class": "library", + "version_property": "validationVersion", + "probe": { + "group_path": "io/spine/tools", + "artifact": "validation-java" + }, + "state": "published-remote", + "target_version": "2.0.0-SNAPSHOT.465", + "pin_vector": { + "ProtoTap": "0.17.3", + "base-libraries": "2.0.0-SNAPSHOT.461", + "compiler": "2.0.0-SNAPSHOT.074", + "logging": "2.0.0-SNAPSHOT.426", + "reflect": "2.0.0-SNAPSHOT.202", + "testlib": "2.0.0-SNAPSHOT.215", + "tool-base": "2.0.0-SNAPSHOT.426" + }, + "adapt_rounds": 0, + "built": { + "head": "5f3f2ef48bece50a1511b036b50dc65381d12e04", + "at": "2026-10-17T01:47:54Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/validation/pull/332", + "head": "5f3f2ef48bece50a1511b036b50dc65381d12e04" + }, + "parked": null + }, + "core-jvm-compiler": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "compiler", + "logging", + "reflect", + "testlib", + "tool-base", + "validation" + ], + "class": "library", + "version_property": "coreJvmCompilerVersion", + "probe": { + "group_path": "io/spine/tools", + "artifact": "core-jvm-gradle-plugin" + }, + "state": "pr-open", + "target_version": "2.0.0-SNAPSHOT.098", + "pin_vector": { + "ProtoTap": "0.17.3", + "base-libraries": "2.0.0-SNAPSHOT.461", + "compiler": "2.0.0-SNAPSHOT.074", + "logging": "2.0.0-SNAPSHOT.426", + "reflect": "2.0.0-SNAPSHOT.202", + "testlib": "2.0.0-SNAPSHOT.215", + "tool-base": "2.0.0-SNAPSHOT.426", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 1, + "built": { + "head": "50bbdfdc7b2f34d324495baff3b9ce02a1c2a310", + "at": "2026-10-16T23:04:25Z" + }, + "pr": { + "url": "https://github.com/SpineEventEngine/core-jvm-compiler/pull/119", + "head": "50bbdfdc7b2f34d324495baff3b9ce02a1c2a310" + }, + "parked": null + }, + "base-types": { + "upstreams": [ + "base-libraries", + "compiler", + "core-jvm-compiler", + "testlib", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-base-types" + }, + "state": "pr-ready", + "target_version": "2.0.0-SNAPSHOT.227", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "compiler": "2.0.0-SNAPSHOT.074", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "testlib": "2.0.0-SNAPSHOT.215", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 1, + "built": { + "head": "8cfdadcb777933dd664588e1747a1a40dac388dd", + "at": "2026-10-19T17:59:49Z" + }, + "pr": null, + "parked": null + }, + "change": { + "upstreams": [ + "base-libraries", + "compiler", + "core-jvm-compiler", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-change" + }, + "state": "pr-ready", + "target_version": "2.0.0-SNAPSHOT.209", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "compiler": "2.0.0-SNAPSHOT.074", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 2, + "built": { + "head": "6f74f8b6afbb88088cd04b5d003df2048d90d086", + "at": "2026-10-16T22:37:21Z" + }, + "pr": null, + "parked": null + }, + "time": { + "upstreams": [ + "ProtoTap", + "base-libraries", + "compiler", + "core-jvm-compiler", + "logging", + "testlib", + "tool-base", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-time" + }, + "state": "pr-ready", + "target_version": "2.0.0-SNAPSHOT.252", + "pin_vector": { + "ProtoTap": "0.17.3", + "base-libraries": "2.0.0-SNAPSHOT.461", + "compiler": "2.0.0-SNAPSHOT.074", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "logging": "2.0.0-SNAPSHOT.426", + "testlib": "2.0.0-SNAPSHOT.215", + "tool-base": "2.0.0-SNAPSHOT.426", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 2, + "built": { + "head": "d73aa1d78248b05880419143bddb3d83d116364c", + "at": "2026-10-16T22:38:44Z" + }, + "pr": null, + "parked": null + }, + "money": { + "upstreams": [ + "base-libraries", + "compiler", + "core-jvm-compiler", + "testlib", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-money" + }, + "state": "pr-ready", + "target_version": "2.0.0-SNAPSHOT.13", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "compiler": "2.0.0-SNAPSHOT.074", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "testlib": "2.0.0-SNAPSHOT.215", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 1, + "built": { + "head": "d435a853f42a6aead6ed6733017d8a3f57690603", + "at": "2026-10-16T22:39:04Z" + }, + "pr": null, + "parked": null + }, + "core-jvm": { + "upstreams": [ + "base-libraries", + "base-types", + "change", + "core-jvm-compiler", + "logging", + "reflect", + "testlib", + "time", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-core" + }, + "state": "pr-ready", + "target_version": "2.0.0-SNAPSHOT.572", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "base-types": "2.0.0-SNAPSHOT.227", + "change": "2.0.0-SNAPSHOT.209", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "logging": "2.0.0-SNAPSHOT.426", + "reflect": "2.0.0-SNAPSHOT.202", + "testlib": "2.0.0-SNAPSHOT.215", + "time": "2.0.0-SNAPSHOT.252", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 2, + "built": { + "head": "1e156115678edf65328bae253816bc60eed84e36", + "at": "2026-10-19T18:05:12Z" + }, + "pr": null, + "parked": null + }, + "jdbc-storage": { + "upstreams": [ + "core-jvm", + "core-jvm-compiler", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine", + "artifact": "spine-rdbms" + }, + "state": "pr-ready", + "target_version": "2.0.0-SNAPSHOT.131", + "pin_vector": { + "core-jvm": "2.0.0-SNAPSHOT.572", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 1, + "built": { + "head": "ccfb397ddda4df12acaf275ab019c0cb9e16c9d6", + "at": "2026-10-16T22:46:46Z" + }, + "pr": null, + "parked": null + }, + "gcloud-jvm": { + "upstreams": [ + "base-libraries", + "base-types", + "compiler", + "core-jvm", + "core-jvm-compiler", + "logging", + "testlib", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/gcloud", + "artifact": "spine-datastore" + }, + "state": "pr-ready", + "target_version": "2.0.0-SNAPSHOT.241", + "pin_vector": { + "base-libraries": "2.0.0-SNAPSHOT.461", + "base-types": "2.0.0-SNAPSHOT.227", + "compiler": "2.0.0-SNAPSHOT.074", + "core-jvm": "2.0.0-SNAPSHOT.572", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "logging": "2.0.0-SNAPSHOT.426", + "testlib": "2.0.0-SNAPSHOT.215", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 1, + "built": { + "head": "cbe4787e7e482cb2916b742cfa5064b99d82b26b", + "at": "2026-10-19T18:07:22Z" + }, + "pr": null, + "parked": null + }, + "delivery": { + "upstreams": [ + "compiler", + "core-jvm", + "core-jvm-compiler", + "logging", + "testlib", + "time", + "validation" + ], + "class": "library", + "version_property": "versionToPublish", + "probe": { + "group_path": "io/spine/delivery", + "artifact": "spine-delivery-server" + }, + "state": "pr-ready", + "target_version": "0.24.1", + "pin_vector": { + "compiler": "2.0.0-SNAPSHOT.074", + "core-jvm": "2.0.0-SNAPSHOT.572", + "core-jvm-compiler": "2.0.0-SNAPSHOT.098", + "logging": "2.0.0-SNAPSHOT.426", + "testlib": "2.0.0-SNAPSHOT.215", + "time": "2.0.0-SNAPSHOT.252", + "validation": "2.0.0-SNAPSHOT.465" + }, + "adapt_rounds": 1, + "built": { + "head": "799a899d46648b5d5db6bfb0ef54ae9932440ded", + "at": "2026-10-16T23:00:09Z" + }, + "pr": null, + "parked": null + }, + "BuildSpeed": { + "upstreams": [ + "core-jvm-compiler" + ], + "class": "verify-only", + "version_property": "", + "probe": { + "group_path": "", + "artifact": "" + }, + "state": "parked", + "target_version": null, + "pin_vector": {}, + "adapt_rounds": 0, + "built": null, + "pr": null, + "parked": "verify-only: SDK versions arrive as CI environment variables substituted into settings.gradle.kts.template, so there are no file pins to apply, no version to bump, and no artifact to publish" + } + }, + "excluded": {}, + "closing_pr": null +} diff --git a/.agents/tasks/archive/cascade-refresh-a0cbbbf.md b/.agents/tasks/archive/cascade-refresh-a0cbbbf.md new file mode 100644 index 0000000..8976f84 --- /dev/null +++ b/.agents/tasks/archive/cascade-refresh-a0cbbbf.md @@ -0,0 +1,45 @@ +--- +slug: cascade-refresh-a0cbbbf +branch: cascade-refresh-a0cbbbf +owner: cascade +status: in-progress +started: 2026-10-15 +--- + +# Wave `cascade-refresh-a0cbbbf` (refresh) + +Machine state: [`cascade-refresh-a0cbbbf.json`](cascade-refresh-a0cbbbf.json). Park reasons and decisions land here. + +## Repos + +- [ ] base-libraries +- [ ] reflect +- [ ] logging +- [ ] testlib +- [ ] dokka-tools +- [ ] tool-base +- [ ] ProtoTap +- [ ] compiler +- [ ] validation +- [ ] core-jvm-compiler +- [ ] base-types +- [ ] change +- [ ] time +- [ ] money +- [ ] core-jvm +- [ ] jdbc-storage +- [ ] gcloud-jvm +- [ ] delivery +- [ ] BuildSpeed + +## Log + +- 2026-10-15 — wave planned. +- 2026-10-15 — **parked** `BuildSpeed`: verify-only: SDK versions arrive as CI environment variables substituted into settings.gradle.kts.template, so there are no file pins to apply, no version to bump, and no artifact to publish +- 2026-10-15 — **parked** `core-jvm-compiler`: awaiting owner decision: 5 tests in tests/entity-queries (EntityQueryBuilderTest) assert FieldMask behavior that base-libraries#964 made a deprecated no-op ('Please remove the call'); deleting them was blocked as test removal +- 2026-10-16 — resumed `core-jvm-compiler`. +- 2026-10-16 — **parked** `validation`: waiting for a complete compiler publish: compiler .072's publish aborted at test-env's PublicationChecksums (config#764) before compiler-testlib uploaded; a compiler fix + config update + new compiler version are in progress, after which validation, core-jvm-compiler and time re-pin to it +- 2026-10-16 — retargeted `compiler` to `2.0.0-SNAPSHOT.074`: the publish of the wave's `.072` (compiler#91) failed midway at test-env's PublicationChecksums, leaving `compiler-testlib` unpublished; compiler#92 fixed the attestation and published `.074` completely. The local wave branch was fast-forwarded to master; downstream repos re-pin to `.074`. +- 2026-10-16 — resumed `validation`. +- 2026-10-19 — `base-types` collided: base-types#56 took `2.0.0-SNAPSHOT.226` on master. Merged master into the wave branch (the wave already carried its change) and re-bumped to `.227`; `core-jvm` and `gcloud-jvm` re-pinned. +- 2026-10-21 — superseded: the wave restarts as `cascade-refresh-604420c` with `config@604420c` (Gradle 9.8.2, Kotlin capped at the Gradle-embedded 2.4.10). Published here: base-libraries .461, reflect .202, logging .426, testlib .215, dokka-tools .11, tool-base .426, ProtoTap 0.17.3, compiler .074, validation .465. Unshipped branches carry over; core-jvm-compiler#119 closed. diff --git a/.agents/tasks/cascade.md b/.agents/tasks/cascade.md index 64c1a5b..60240d6 100644 --- a/.agents/tasks/cascade.md +++ b/.agents/tasks/cascade.md @@ -250,7 +250,7 @@ on-hold throttling task's cache/proxy phases before unattended cloud waves. 1. **Graph**: `cascade graph` output renders on GitHub, matches the known order (base-libraries → logging/testlib/base-types/change/time → tool-base/ProtoTap/ compiler → validation → core-jvm-compiler → core-jvm → jdbc-storage/gcloud-jvm/ - delivery-server; BuildSpeed verify-only); `graph --check` flags drift after a + delivery; BuildSpeed verify-only); `graph --check` flags drift after a hand-edited edge. 2. **Unit-level**: `plan --kind refresh` reads the graph and freezes edges; `plan --kind ripple --seed base-libraries@` scopes to the downstream closure only. @@ -365,7 +365,7 @@ and refuses to run on drift. 132-edge set stays canonical in a machine-readable ```edges block (each edge is a direct pin the wave advances, so none can be dropped from the data — only from the rendering). - * `delivery-server` publishes **only to GitHub Packages** (proprietary) — + * `delivery` publishes **only to GitHub Packages** (proprietary) — probes gained a per-repo registry field; reading GH Packages needs the `read:packages` token scope (playbook prerequisite; `plan` warns, ship hard-requires). diff --git a/.agents/tasks/toolbase-split-migration.md b/.agents/tasks/toolbase-split-migration.md index 4b32bc9..154d416 100644 --- a/.agents/tasks/toolbase-split-migration.md +++ b/.agents/tasks/toolbase-split-migration.md @@ -84,7 +84,7 @@ migrating: `core-java-1x` (2023-06-29), `model-tools` (2022-10-04). nothing from the artifact. The fix is to drop the entry, or replace it with the specific modules if the forced version still matters: -- [ ] `base-types` · `bootstrap` · `change` · `core-jvm` · `delivery-server` +- [ ] `base-types` · `bootstrap` · `change` · `core-jvm` · `delivery` - [ ] `dokka-tools` · `gcloud-jvm` · `jdbc-storage` · `logging` · `model-compiler` - [ ] `reflect` · `time` · `validation` · `web` diff --git a/.claude/settings.json b/.claude/settings.json index 7f8e7cc..e4b756f 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -5,6 +5,7 @@ "allow": [ "Edit(version.gradle.kts)", "Bash(./gradlew:*)", + "Bash(./gradlew clean:*)", "Bash(./config/gradlew:*)", "Bash(git status:*)", "Bash(git diff:*)", @@ -14,6 +15,7 @@ "Bash(git switch:*)", "Bash(git checkout:*)", "Bash(git add:*)", + "Bash(git commit:*)", "Bash(git restore:*)", "Bash(git stash:*)", "Bash(git fetch:*)", @@ -38,7 +40,6 @@ "Bash(./config/migrate)", "Skill(pre-pr)", "Skill(pre-pr:*)", - "Bash(git commit:*)", "Bash(./cascade:*)", "Bash(gh pr view:*)", "Bash(gh pr list:*)", @@ -47,17 +48,22 @@ "deny": [ "Bash(git reset --hard:*)", "Bash(git clean -fdx:*)", - "Bash(git push --force:*)", - "Bash(git push -f:*)", - "Bash(git push --force-with-lease:*)", "Bash(rm -rf /:*)", "Bash(rm -rf ~:*)", "Bash(gh pr merge:*)", "Bash(gh release create:*)" ], "ask": [ + "Bash(git push --force*)", + "Bash(git push * --force*)", + "Bash(git push -f*)", + "Bash(git push * -f*)", + "Bash(git push -uf*)", + "Bash(git push * -uf*)", + "Bash(git push * +*)", + "Bash(git push --mirror*)", + "Bash(git push * --mirror*)", "Bash(git rebase:*)", - "Bash(git tag:*)", "Bash(git merge:*)", "Bash(git cherry-pick:*)", "Bash(./gradlew publish:*)", diff --git a/.gitmodules b/.gitmodules index 6f34f85..f95103f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -58,9 +58,9 @@ [submodule "agents"] path = agents url = https://github.com/SpineEventEngine/agents.git -[submodule "delivery-server"] - path = delivery-server - url = https://github.com/SpineEventEngine/delivery-server.git +[submodule "delivery"] + path = delivery + url = https://github.com/SpineEventEngine/delivery.git [submodule "BuildSpeed"] path = BuildSpeed url = https://github.com/SpineEventEngine/BuildSpeed.git diff --git a/AGENTS.md b/AGENTS.md index 8c5f619..dc6927f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,17 +20,29 @@ Shared skills, scripts, and guidelines come from the `.agents/shared` submodule (and some shallow clones / cloud checkouts) start with NO submodules checked out, so those symlinks dangle and no skills are found. Bootstrap such a tree with **`./init-submodules`** — a root script that materializes the missing -*config-managed* submodules at their pinned commits: `config` itself, plus every -submodule that declares a tracked `branch` in `.gitmodules` (`.agents/shared`, and -any shared submodule added later) — the same rule `./config/pull` uses to decide -what it floats. Submodules the consumer owns (a Hugo theme, a vendored library, -doc-example submodules, …) declare no tracked branch and are left untouched, so the -automatic `SessionStart` run never tries to clone — or fail on credentials for — a -submodule this project does not manage. It depends on no pre-existing `config` -submodule, so it works before `./config/pull` (which lives inside the `config` -submodule) can. Claude Code runs it automatically via a `SessionStart` hook; other -agents and humans run it by hand, then `./config/pull` to float the shared submodules -to their branch tips. +*config-managed* submodules at the tip of their branch (`master` for `config`, the +tracked `branch` for the rest): `config` itself, plus every submodule that declares +a tracked `branch` in `.gitmodules` (`.agents/shared`, and any shared submodule +added later) — the same rule `./config/pull` uses to decide what it floats. It +deliberately does not stop at the commit the superproject pins for such a +submodule: nothing keeps that pin current — `ignore = all` hides the submodule from +`git status`, and `./config/pull` floats the on-disk copy without committing the +moved gitlink — so materializing the pin as-is means months-old hook scripts, +skills, and guidelines. If the float fails — a branch the remote does not have, +say — a warning is printed and the submodule is left wherever the steps that +did succeed put it, at worst the pin, so a session still starts with a usable +tree; it is not retried on a later run, which is why that warning points at +`./config/pull`. With no network at all the clone itself fails and the +submodule is left uninitialized, as it was before. Submodules the consumer owns +(a Hugo theme, a vendored library, doc-example submodules, …) declare no tracked +branch and are left untouched, so the automatic `SessionStart` run never tries +to clone — or fail on credentials for — a submodule this project does not +manage. A submodule that is already checked out is left exactly as it is, which +keeps repeated runs cheap and offline-safe and never rewinds a tree someone +parked on a particular commit; run `./config/pull` to float those. It depends on +no pre-existing `config` submodule, so it works before `./config/pull` (which +lives inside the `config` submodule) can. Claude Code runs it automatically via +a `SessionStart` hook; other agents and humans run it by hand. ## Commit and history safety diff --git a/README.md b/README.md index 4d09e1c..8266d04 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,11 @@ does against the SDK repositories above. ### Storage & runtime -| Submodule | Role | -|--------------------------------------|--------------------------------------------------------------| -| [`jdbc-storage`][jdbc-storage] | Support for storage in JDBC-compliant databases | -| [`gcloud-jvm`][gcloud-jvm] | Support for Spine-based Kotlin and Java apps on Google Cloud | -| [`delivery-server`][delivery-server] | gRPC services providing sharded message delivery | +| Submodule | Role | +|--------------------------------|--------------------------------------------------------------| +| [`jdbc-storage`][jdbc-storage] | Support for storage in JDBC-compliant databases | +| [`gcloud-jvm`][gcloud-jvm] | Support for Spine-based Kotlin and Java apps on Google Cloud | +| [`delivery`][delivery] | gRPC services providing sharded message delivery | ### Benchmarks & documentation @@ -128,6 +128,6 @@ available skills are described in [`AGENTS.md`](AGENTS.md). [core-jvm]: https://github.com/SpineEventEngine/core-jvm [jdbc-storage]: https://github.com/SpineEventEngine/jdbc-storage [gcloud-jvm]: https://github.com/SpineEventEngine/gcloud-jvm -[delivery-server]: https://github.com/SpineEventEngine/delivery-server +[delivery]: https://github.com/SpineEventEngine/delivery [BuildSpeed]: https://github.com/SpineEventEngine/BuildSpeed [documentation]: https://github.com/SpineEventEngine/documentation diff --git a/cascade b/cascade index 2e7493f..b401ccd 100755 --- a/cascade +++ b/cascade @@ -102,7 +102,7 @@ write_pin_constant() { # repos_all() { echo "base-libraries reflect logging testlib dokka-tools tool-base \ ProtoTap compiler validation core-jvm-compiler base-types change time money \ -core-jvm jdbc-storage gcloud-jvm delivery-server BuildSpeed" +core-jvm jdbc-storage gcloud-jvm delivery BuildSpeed" } # Position of a repo in the canonical order (1-based; 0 = unknown). @@ -150,6 +150,25 @@ obj_to_repo() { esac } +# `config`'s accessor aliases for local objects (`BuildExtensions.kt`) that are +# dereferenced as `.member` where the objects are not imported, e.g. +# `classpath(spineCompiler.pluginLib)` in a `buildscript {}` block. +# `obj_of_alias` is the inverse. +alias_of_obj() { + case "$1" in + Compiler) echo spineCompiler ;; + CoreJvmCompiler) echo coreJvmCompiler ;; + *) echo "" ;; + esac +} + +obj_of_alias() { + case "$1" in + spineCompiler) echo Compiler ;; + coreJvmCompiler) echo CoreJvmCompiler ;; + esac +} + # repo -> pin file (relative to a consumer's buildSrc local/ dir). repo_pin_file() { case "$1" in @@ -159,6 +178,7 @@ repo_pin_file() { compiler) echo Compiler.kt ;; core-jvm) echo CoreJvm.kt ;; core-jvm-compiler) echo CoreJvmCompiler.kt ;; + dokka-tools) echo DokkaTools.kt ;; logging) echo Logging.kt ;; ProtoTap) echo ProtoTap.kt ;; reflect) echo Reflect.kt ;; @@ -186,7 +206,7 @@ repo_track_constants() { # marker-artifact: one definitely-published artifact used for registry probes. # class: library (full pipeline) | verify-only (no version/publishing steps). # registry: gcar (public europe-maven.pkg.dev) | github (GitHub Packages — -# delivery-server is proprietary and publishes only there; probes authenticate +# `delivery` is proprietary and publishes only there; probes authenticate # with `gh auth token`). repo_meta() { case "$1" in @@ -207,7 +227,7 @@ repo_meta() { core-jvm) echo "io/spine|spine-core|versionToPublish|library|gcar" ;; jdbc-storage) echo "io/spine|spine-rdbms|versionToPublish|library|gcar" ;; gcloud-jvm) echo "io/spine/gcloud|spine-datastore|versionToPublish|library|gcar" ;; - delivery-server) echo "io/spine/delivery|spine-delivery-server|versionToPublish|library|github" ;; + delivery) echo "io/spine/delivery|spine-delivery-server|versionToPublish|library|github" ;; BuildSpeed) echo "|||verify-only|" ;; *) echo "" ;; esac @@ -292,6 +312,76 @@ registry_has() { # marker_metadata "$1" | grep -q "$2" } +# The `publish.yml` run for the merge commit of the repo's wave PR, as +# " ": `ok`, `failed`, or `running`. The marker artifact alone +# cannot prove a publish completed: a run that fails midway has already uploaded +# some modules (compiler `.072` reached the registry without `compiler-testlib`). +# With no recorded PR, no merge commit, or no run (a seed published by hand, a +# repo without the workflow), the verdict is `ok` and the registry stays the +# only evidence, as before. +iso_epoch() { # -> seconds since the epoch (BSD or GNU `date`) + date -j -u -f '%Y-%m-%dT%H:%M:%SZ' "$1" +%s 2>/dev/null || date -u -d "$1" +%s +} + +publish_verdict() { # + local url sha merged run + url="$(mget "$1" --arg r "$2" '.repos[$r].pr.url // empty')" + [ -n "$url" ] || { echo ok; return; } + read -r sha merged </dev/null || true) +EOF + [ -n "$sha" ] || { echo ok; return; } + run="$(gh run list -R "$ORG/$2" --workflow publish.yml --commit "$sha" --limit 1 \ + --json status,conclusion,url \ + --jq '.[0] // empty | "\(.status) \(if (.conclusion // "") == "" then "none" else .conclusion end) \(.url)"' \ + 2>/dev/null || true)" + if [ -z "$run" ]; then + # GitHub can delay the push event of a merge, or drop it, and then no workflow starts + # for the merge commit. The time `.252` run started 17 minutes late, during an + # incident; an hour without a run means the publication never happened. + if [ -n "$merged" ] && [ $(( $(date +%s) - $(iso_epoch "$merged") )) -gt 3600 ]; then + echo "failed https://github.com/$ORG/$2/commit/$sha (no publish run started)" + else + echo ok + fi + return + fi + set -- $run + case "$1 $2" in + "completed success") echo "ok $3" ;; + completed\ *) echo "failed $3" ;; + *) echo "running $3" ;; + esac +} + +# Has the wave's own PR for the repo merged? Without that, a registry serving the +# wave's target version serves someone else's build: the base branch took the +# same version first (core-jvm `.571`). A ripple seed given as `repo@version` is +# published by hand, and when `gh` itself fails the registry is the only +# evidence; both count as merged. +wave_pr_merged() { # + local ref out + [ "$2" = "$(mget "$1" '.seed.repo // empty')" ] \ + && [ -n "$(mget "$1" '.seed.version // empty')" ] && return 0 + ref="$(mget "$1" --arg r "$2" '.repos[$r].pr.url // empty')" + [ -n "$ref" ] || ref="$(mget "$1" .branch)" + if out="$(gh pr view "$ref" -R "$ORG/$2" --json state --jq .state 2>&1)"; then + [ "$out" = MERGED ] + else + case "$out" in *"no pull requests found"*) return 1 ;; esac + return 0 + fi +} + +# The open PR from the wave branch, or nothing. Unlike `gh pr view `, it +# ignores a merged PR from the same branch name, which a repository re-shipped +# after a failed publish already has. +open_pr_url() { # + gh pr list -R "$ORG/$1" --head "$2" --state open --json url --jq '.[0].url // empty' \ + 2>/dev/null || true +} + # File.kt -> repo (reverse of repo_pin_file); empty when not a repo pin file. pin_file_repo() { # local r @@ -392,8 +482,14 @@ derive_scoped() { done [ -n "$files" ] || continue # shellcheck disable=SC2086 - objs="$(grep -hoE 'io\.spine\.dependency\.local\.[A-Z][A-Za-z]+' $files 2>/dev/null \ - | sed 's/.*\.//' | sort -u)" + objs="$( { grep -hoE 'io\.spine\.dependency\.local\.[A-Z][A-Za-z]+' $files 2>/dev/null \ + | sed 's/.*\.//' + # Optional: most repositories reference no alias, and `grep` + # exits 1 on no match, which `pipefail` would propagate. + grep -hoE '(^|[^.A-Za-z])(spineCompiler|coreJvmCompiler)\.[A-Za-z]' $files 2>/dev/null \ + | grep -oE 'spineCompiler|coreJvmCompiler' \ + | while read -r a; do obj_of_alias "$a"; done || true + } | sort -u)" # Block-aware classification: a reference creates an edge only where # it is a declared dependency. References inside version-management # constructs — force(...), constraints {...}, exclude(...) — create NO @@ -407,7 +503,7 @@ derive_scoped() { up="$(obj_to_repo "$o")" [ -n "$up" ] && [ "$up" != "$repo" ] || continue # shellcheck disable=SC2086 - scopes="$(awk -v obj="$o" ' + scopes="$(awk -v obj="$o" -v alias="$(alias_of_obj "$o")" ' function push(tag) { sp++; tags[sp] = tag; depths[sp] = depth } { line = $0 @@ -416,8 +512,10 @@ derive_scoped() { # and the fully-qualified `io.spine.dependency.local.Obj.member`, # which is the house style inside `buildscript {}` where # file-level imports do not apply. The leading guard still - # rejects an unrelated `Something.Obj.member`. + # rejects an unrelated `Something.Obj.member`. A `config` + # accessor alias (`spineCompiler.member`) counts the same way. use = match(line, "(^|[^.A-Za-z])(io\\.spine\\.dependency\\.local\\.)?" obj "\\.[A-Za-z]") + if (!use && alias != "") use = match(line, "(^|[^.A-Za-z])" alias "\\.[A-Za-z]") if (use) { if (line ~ /(^|[^A-Za-z])test[A-Za-z]*\(/) print "test" else if (line ~ /(^|[^A-Za-z])(api|implementation|compileOnly|compileOnlyApi|runtimeOnly|annotationProcessor|classpath|kapt|ksp)\(/) print "main" @@ -454,11 +552,16 @@ derive_scoped() { # coreJvmCompiler / io.spine.core-jvm -> core-jvm-compiler # io.spine.{artifact-meta,descriptor-set-file, # generated-sources} -> tool-base + # `spineCompiler` and `coreJvmCompiler` (not `prototap`) also appear as + # `id(.pluginId)`. # shellcheck disable=SC2086 { grep -hE '^[[:space:]]*(prototap|spineCompiler|coreJvmCompiler)[[:space:]]*$' $files 2>/dev/null \ | sed 's/[[:space:]]//g' | sort -u \ | sed 's/^prototap$/ProtoTap/; s/^spineCompiler$/compiler/; s/^coreJvmCompiler$/core-jvm-compiler/' + grep -hoE '(^|[^.A-Za-z])id\((spineCompiler|coreJvmCompiler)\.pluginId\)' $files 2>/dev/null \ + | grep -oE 'spineCompiler|coreJvmCompiler' | sort -u \ + | sed 's/^spineCompiler$/compiler/; s/^coreJvmCompiler$/core-jvm-compiler/' grep -hoE '"io\.spine\.(prototap|compiler|core-jvm|artifact-meta|descriptor-set-file|generated-sources)"' $files 2>/dev/null \ | tr -d '"' | sort -u \ | sed 's/^io\.spine\.prototap$/ProtoTap/; s/^io\.spine\.compiler$/compiler/; s/^io\.spine\.core-jvm$/core-jvm-compiler/; s/^io\.spine\..*$/tool-base/' @@ -794,6 +897,12 @@ active_wave() { basename "$m" .json } +# A manifest that lists no repos reads as a finished wave, so `next` would say CLOSE. +require_intact() { # + [ "$(mget "$1" '(.repos // {}) | length > 0' 2>/dev/null)" = true ] \ + || die "the manifest of '$1' lists no repos — restore it: git checkout -- $(manifest_path "$1")" +} + # mget [jq-args...] — the program is always the LAST argument. mget() { local wave="$1"; shift @@ -805,8 +914,15 @@ mget() { } mset() { # [--arg k v ...] local wave="$1" prog="$2"; shift 2 - local p tmp; p="$(manifest_path "$wave")"; tmp="$p.tmp" - jq "$@" "$prog" "$p" > "$tmp" && mv "$tmp" "$p" + local p tmp; p="$(manifest_path "$wave")" + # A private temp file: with a shared one, concurrent runs (the driver and a manual + # call) truncated each other's output and moved an empty manifest into place. + tmp="$(mktemp "$p.XXXXXX")" + if jq "$@" "$prog" "$p" > "$tmp" && [ -s "$tmp" ]; then + mv "$tmp" "$p" + else + rm -f "$tmp"; die "could not update $p" + fi } wave_repos() { mget "$1" '.repos | keys[]'; } @@ -1022,10 +1138,31 @@ derive_state() { # -> prints state # the registry, this repository is done. Without this the derivation reads a # MERGED branch as `pending` -- after the merge `origin/$BASE..$branch` is # empty, so the branch looks like it never had work -- and the wave asks to - # prep and rebuild a repository it has already shipped. + # prep and rebuild a repository it has already shipped. The exception is + # `collision`: while the wave's PR for the repository is unmerged, the + # registry's copy is the base branch's build of the same version, so the + # repository must re-bump. local tvp; tvp="$(mget "$wave" --arg r "$repo" '.repos[$r].target_version // empty')" if [ -n "$tvp" ] && registry_has "$repo" "$tvp"; then - echo published-remote; return + wave_pr_merged "$wave" "$repo" || { echo collision; return; } + case "$(publish_verdict "$wave" "$repo")" in + failed\ *) echo publish-failed; return ;; + running\ *) echo merged; return ;; + *) echo published-remote; return ;; + esac + fi + + # The wave's PR merged, but the registry does not serve the target yet: the + # publish run is pending, or it failed before uploading the marker (validation + # `.465` hit HTTP 429 while configuring). Commits pushed to the branch after + # the merge must not read as a fresh branch that needs `prep` again. + if [ -n "$tvp" ] && [ -n "$(mget "$wave" --arg r "$repo" '.repos[$r].pr.url // empty')" ] \ + && wave_pr_merged "$wave" "$repo"; then + case "$(publish_verdict "$wave" "$repo")" in + failed\ *) echo publish-failed ;; + *) echo merged ;; + esac + return fi local subjects @@ -1083,18 +1220,29 @@ derive_state() { # -> prints state s_status="$(sed -n 's/^status=//p' "$gitdir/pre-pr.ok" 2>/dev/null || true)" { [ "$s_status" = PASS ] && [ "$s_head" = "$head" ]; } || { echo published-local; return; } - # pr-open / merged. - local prstate - prstate="$(gh pr view "$branch" -R "$ORG/$repo" --json state --jq .state 2>/dev/null || true)" + # pr-open / merged. A PR merged from another head is the previous ship of + # the reused wave branch (a new version after a failed publish): the + # current head has no PR yet. + local prstate prhead + read -r prstate prhead </dev/null || true) +EOF + [ "$prstate" = MERGED ] && [ "$prhead" != "$head" ] && prstate="" case "$prstate" in MERGED) ;; OPEN) echo pr-open; return ;; *) echo pr-ready; return ;; esac - # published-remote (library): registry serves the target. + # published-remote (library): registry serves the target and the publish run + # completed. if [ "$cls" = library ]; then local tv; tv="$(mget "$wave" --arg r "$repo" '.repos[$r].target_version')" + case "$(publish_verdict "$wave" "$repo")" in + failed\ *) echo publish-failed; return ;; + running\ *) echo merged; return ;; + esac registry_has "$repo" "$tv" || { echo merged; return; } echo published-remote; return fi @@ -1122,10 +1270,15 @@ cmd_status() { done fi mset "$wave" '.repos[$r].state = $s' --arg r "$repo" --arg s "$st" + local note + note="$(mget "$wave" --arg r "$repo" '.repos[$r].parked // ""')" + [ "$st" = publish-failed ] && note="publish run failed: $(publish_verdict "$wave" "$repo" | cut -d' ' -f2)" + [ "$st" = collision ] \ + && note="$BASE took this version first — merge $BASE, then: ./cascade refresh $repo" printf '%-20s %-17s %-24s %s\n' "$repo" "${blocked:+blocked}${blocked:-$st}" \ "$(mget "$wave" --arg r "$repo" '.repos[$r].target_version // empty' \ | grep . || version_of "$repo" "$wbranch" 2>/dev/null | grep . || echo "—")" \ - "${blocked:-$(mget "$wave" --arg r "$repo" '.repos[$r].parked // ""')}" + "${blocked:-$note}" done [ "$(mget "$wave" '.halted // empty')" != "" ] && warn "WAVE HALTED: $(mget "$wave" .halted)" return 0 @@ -1138,6 +1291,7 @@ cmd_status() { cmd_next() { local wave json=0 wave="$(active_wave)" + require_intact "$wave" [ "${1:-}" = "--json" ] && json=1 cmd_status "$wave" >/dev/null # refresh derived states first @@ -1186,6 +1340,13 @@ cmd_next() { else actions="$actions WAIT-UPSTREAM:$repo"; fi ;; pr-open) actions="$actions WAIT-REVIEW:$repo" ;; merged) actions="$actions WAIT-PUBLISH:$repo" ;; + # The publish run failed after the merge; a human fixes the build + # and publishes a new version. Downstream repos keep waiting. + publish-failed) actions="$actions FIX-PUBLISH:$repo" ;; + # The base branch published the wave's target version first; the + # driver merges the base branch and re-bumps. Downstream repos keep + # waiting. + collision) actions="$actions REBUMP:$repo" ;; parked|blocked|published-remote) ;; esac done @@ -1220,12 +1381,22 @@ cmd_prep() { git switch -q -c "$branch" 2>/dev/null || git switch -q "$branch" fi + # Check `config` out at the commit the branch records: `git pull` moves the + # recorded pointer but not the checkout (a false "working tree not clean" error + # below), and an uninitialized `config` would be mistaken for a repo that does + # not consume it. + if git config -f .gitmodules --get submodule.config.path >/dev/null 2>&1; then + git submodule update --init --quiet -- config + fi + # ./config/pull — commit only what it actually changed. A repo that does not # consume `config` (its own hand-written buildSrc, no `config` submodule) has # nothing to pull; skipping keeps it a wave participant instead of a hard error. if [ ! -x ./config/pull ]; then note "$repo does not consume \`config\` — no pull step" - elif ! git log --format=%s "origin/$BASE..HEAD" | grep -q '^Update shared agent tooling'; then + # A here-string, not a pipe: `grep -q` exits at the first match, and under + # `pipefail` the SIGPIPE `git log` may then take reads as "not pulled yet". + elif ! grep -q '^Update shared agent tooling' <<<"$(git log --format=%s "origin/$BASE..HEAD")"; then [ -z "$(git status --porcelain)" ] \ || die "working tree not clean before ./config/pull at $repo — commit or stash first" ./config/pull @@ -1407,6 +1578,13 @@ cmd_ship() { for up in $(upstreams_of "$wave" "$repo"); do pv="$(mget "$wave" --arg r "$repo" --arg u "$up" '.repos[$r].pin_vector[$u] // empty')" [ -n "$pv" ] || continue + # Re-bump drift the registry cannot reveal: the upstream re-bumped after + # this repo was pinned (a collision or a failed publish), but the old + # pinned version is still public. + local tvu; tvu="$(mget "$wave" --arg r "$up" '.repos[$r].target_version // empty')" + if [ -n "$tvu" ] && [ "$tvu" != "$pv" ]; then + warn "upstream $up re-bumped ($pv -> $tvu) — run: ./cascade refresh $repo"; exit 3 + fi if ! registry_has "$up" "$pv"; then # Drift check: did the upstream move past our pin (re-bump)? local upcur; upcur="$(version_of "$up" "$branch" 2>/dev/null || version_of "$up")" @@ -1415,6 +1593,21 @@ cmd_ship() { fi warn "waiting: $up $pv not yet in the registry"; exit 4 fi + # A registry copy the wave did not publish is the base branch's build of + # that version. + wave_pr_merged "$wave" "$up" || { + warn "upstream $up $pv: the registry serves $BASE's build, not the wave's" + warn "re-bump $up first (REBUMP in ./cascade next), then: ./cascade refresh $repo" + exit 3; } + # The marker alone may come from a publish that failed midway. + local verdict; verdict="$(publish_verdict "$wave" "$up")" + case "$verdict" in + failed\ *) + warn "upstream $up $pv: its publish run failed (${verdict#failed }), so some modules may be missing" + warn "fix and re-publish $up, re-pin with ./cascade refresh $repo, then ship again" + exit 3 ;; + running\ *) warn "waiting: $up $pv publish still running (${verdict#running })"; exit 4 ;; + esac done # Floor gate: every version pinned in this repo's own pin files must be @@ -1442,17 +1635,23 @@ EOF fi git checkout -- docs/dependencies/ 2>/dev/null || true - if gh pr view "$branch" -R "$ORG/$repo" --json url >/dev/null 2>&1; then + local url; url="$(open_pr_url "$repo" "$branch")" + if [ -n "$url" ]; then git push -q origin "$branch" - note "PR already open: $(gh pr view "$branch" -R "$ORG/$repo" --json url --jq .url)" + note "PR already open: $url" else git push -u origin "$branch" - gh pr create --base "$BASE" --head "$branch" \ + # A draft: the organization's ruleset requests a Copilot review for every + # pull request opened as ready, before CI has run. The driver marks the + # draft ready once all its checks pass, which requests that review then. + gh pr create --draft --base "$BASE" --head "$branch" \ --title "$(pr_title)" \ --body-file "$SUMMIT/docs/rollout/rebuild-pr-body.md" + url="$(open_pr_url "$repo" "$branch")" fi + [ -n "$url" ] || die "no open PR from '$branch' in $ORG/$repo after ship" mset "$wave" '.repos[$r].pr = {url: $u, head: $h}' --arg r "$repo" \ - --arg u "$(gh pr view "$branch" -R "$ORG/$repo" --json url --jq .url)" --arg h "$head" + --arg u "$url" --arg h "$head" checkpoint_state "$wave" || true } @@ -1471,6 +1670,18 @@ pr_title() { cmd_refresh() { local repo="${1:?usage: cascade refresh }" wave wave="$(active_wave)" + # After a failed publish the wave branch has merged: start the repository + # over from the base branch, so prep pulls `config` again and bumps past the + # partly published version. + if [ "$(derive_state "$wave" "$repo")" = publish-failed ]; then + local dir="$SUMMIT/$repo" branch + branch="$(mget "$wave" .branch)" + git -C "$dir" fetch --quiet origin "$BASE" + git -C "$dir" checkout -q "$BASE" + git -C "$dir" pull --ff-only --quiet origin "$BASE" + git -C "$dir" branch -d "$branch" # refuses a branch the base lacks + mset "$wave" '.repos[$r].pr = null | .repos[$r].target_version = null' --arg r "$repo" + fi # Clear the recorded pins so prep re-reads the upstreams' current versions, # then invalidate the build record (the branch will gain commits). mset "$wave" '.repos[$r].pin_vector = {} | .repos[$r].built = null' --arg r "$repo" @@ -1536,6 +1747,7 @@ cmd_await() { cmd_close() { local wave ship=0 wave="$(active_wave)" + require_intact "$wave" [ "${1:-}" = "--ship" ] && ship=1 local cfg="$SUMMIT/config" vbranch repo tv pf c cur cur_re vbranch="$(mget "$wave" .wave)-vector" @@ -1596,7 +1808,9 @@ cmd_close() { { [ "$s_status" = PASS ] && [ "$s_head" = "$chead" ]; } \ || { warn "no PASS pre-pr sentinel for HEAD in config — run the pre-pr skill there, then: ./cascade close --ship"; exit 3; } git push -u origin "$vbranch" - gh pr create -R "$ORG/config" --base "$BASE" --head "$vbranch" \ + # A draft, as in `ship`: the ruleset requests a Copilot review for every PR + # opened as ready. Mark it ready once all its checks pass. + gh pr create --draft -R "$ORG/config" --base "$BASE" --head "$vbranch" \ --title "Record the \`$wave\` version vector" \ --body "Advances \`io.spine.dependency.local\` pins to the versions published by wave \`$wave\`. All versions verified present in the registry before this PR was opened." mset "$wave" '.closing_pr = $u' --arg u "$(gh pr view "$vbranch" -R "$ORG/config" --json url --jq .url)" diff --git a/config b/config index 3409e7d..604420c 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 3409e7dd193e8cb7da82467d01039140c79f84ab +Subproject commit 604420cd7eab82b382c9a5c1d812be454552c27c diff --git a/delivery-server b/delivery similarity index 100% rename from delivery-server rename to delivery diff --git a/docs/dependency-graph.md b/docs/dependency-graph.md index aba456b..221af17 100644 --- a/docs/dependency-graph.md +++ b/docs/dependency-graph.md @@ -58,7 +58,7 @@ consumed at runtime. 15. `core-jvm` 16. `jdbc-storage` 17. `gcloud-jvm` -18. `delivery-server` +18. `delivery` 19. `BuildSpeed` ## Documentation tooling @@ -93,7 +93,7 @@ flowchart BT core-jvm --> base-types core-jvm --> change validation --> compiler - delivery-server --> core-jvm + delivery --> core-jvm gcloud-jvm --> core-jvm jdbc-storage --> core-jvm BuildSpeed --> core-jvm-compiler @@ -125,12 +125,12 @@ flowchart BT time --> logging ProtoTap --> testlib base-types --> testlib - delivery-server --> testlib + delivery --> testlib gcloud-jvm --> testlib money --> testlib time --> testlib validation --> testlib - delivery-server --> time + delivery --> time ``` @@ -191,7 +191,7 @@ of the literal in the repository's `version.gradle.kts`. | `core-jvm` | library | gcar : `io.spine:spine-core` | `versionToPublish` | | `jdbc-storage` | library | gcar : `io.spine:spine-rdbms` | `versionToPublish` | | `gcloud-jvm` | library | gcar : `io.spine.gcloud:spine-datastore` | `versionToPublish` | -| `delivery-server` | library | github : `io.spine.delivery:spine-delivery-server` | `versionToPublish` | +| `delivery` | library | github : `io.spine.delivery:spine-delivery-server` | `versionToPublish` | | `BuildSpeed` | verify-only | — | — | `documentation` is not on the graph: it is the documentation area of the @@ -255,6 +255,7 @@ compiler validation core-jvm base-libraries core-jvm base-types core-jvm change +core-jvm compiler core-jvm core-jvm-compiler core-jvm logging core-jvm reflect @@ -271,13 +272,13 @@ core-jvm-compiler testlib core-jvm-compiler time core-jvm-compiler tool-base core-jvm-compiler validation -delivery-server compiler -delivery-server core-jvm -delivery-server core-jvm-compiler -delivery-server logging -delivery-server testlib -delivery-server time -delivery-server validation +delivery compiler +delivery core-jvm +delivery core-jvm-compiler +delivery logging +delivery testlib +delivery time +delivery validation dokka-tools base-libraries gcloud-jvm base-libraries gcloud-jvm base-types @@ -287,6 +288,7 @@ gcloud-jvm core-jvm-compiler gcloud-jvm logging gcloud-jvm testlib gcloud-jvm validation +jdbc-storage compiler jdbc-storage core-jvm jdbc-storage core-jvm-compiler jdbc-storage validation diff --git a/docs/rollout/rebuild.md b/docs/rollout/rebuild.md index 186653d..7d1c1dc 100644 --- a/docs/rollout/rebuild.md +++ b/docs/rollout/rebuild.md @@ -34,7 +34,7 @@ Wave kinds: set it to the JDK 17 path directly, or use your distribution's alternatives mechanism. - `gh auth status` shows a token with `repo` + `workflow` scope, plus - `read:packages` — `delivery-server` publishes to GitHub Packages, and + `read:packages` — `delivery` publishes to GitHub Packages, and `ship` and `close` cannot verify its artifacts without that scope. - Builds run **strictly sequentially** — never start a second Gradle build while a wave build runs. Maven Central consumption blocks (HTTP 429) are machine-wide @@ -99,17 +99,41 @@ and execute what it prints, in order: repo — three adapt→rebuild rounds, then auto-park). Exit 5 = throttling halt. - `PREPR:` → run the `pre-pr` skill inside the repo (writes the sentinel). - `SHIP:` → `./cascade ship ` — sentinel gate, upstream-published - gate, floor gate (every version the repo pins — forward upstreams *and* - published floors — proven in the registry, derived from the pin files by - content, since floors never appear in the manifest), drift gate, push, PR. - Exit 4 means an upstream is not published yet: wait, don't force. + gate (the upstream has not re-bumped since this repo pinned it, the registry + serves its version *and* the upstream's wave PR for it has merged, and its + `publish.yml` run for the merge commit succeeded), floor gate (every version + the repo pins — forward upstreams *and* published floors — proven in the + registry, derived from the pin files by content, since floors never appear in + the manifest), drift gate, push, PR. Exit 4 means an upstream is not + published yet: wait, don't force. Exit 3 from the upstream gate (the upstream + re-bumped, collided with the base branch, or failed to publish) means + re-pinning with `./cascade refresh ` once the upstream is fixed. + The PR opens as a draft, because the organization's ruleset requests a + Copilot review for every PR opened as ready, before CI has run. Once all its + checks pass, mark it ready (`gh pr ready `), which requests that review + then, and enable auto-merge. +- `FIX-PUBLISH:` → the repo's `publish.yml` run failed after its PR + merged, or never started: GitHub can delay or drop a merge's push event, and + an hour without a run counts as a failure. A run that fails midway may already have uploaded some modules, so + the registry's marker artifact proves nothing; `./cascade status` links the + run. Fix the cause (often in `config`), then run `./cascade refresh `: + it branches again from the base branch, pulls `config`, and bumps to a new + version. Build, pre-pr, and ship it as usual, and re-pin each downstream repo + with `./cascade refresh `. Downstream repos wait meanwhile. +- `REBUMP:` → the registry serves the repo's wave version, but the repo's + wave PR has not merged: the base branch took the same version first, so the + registry holds the base branch's build, not the wave's. Merge the base branch + into the wave branch, resolving a `version.gradle.kts` conflict by the + `bump-version` Conflict Rule, then run `./cascade refresh ` to re-bump + past the base branch's version. Rebuild the repo, and re-pin each downstream + repo with `./cascade refresh `. Downstream repos wait meanwhile. - `WAIT-*` only → `./cascade await` (background it) — exits the moment the action set changes; then run `next` again. - `CLOSE` → `./cascade close` — stages the final vector in `config` (floors included, now provably published), exit 3 → `pre-pr` in config → `./cascade close --ship` opens the closing PR. -Humans do exactly: review/merge PRs, resolve parks (`./cascade resume`), and +Humans do exactly: review/merge PRs, resolve parks (`./cascade resume`), fix failed publishes, and acknowledge throttle halts. Everything else is the loop. ## 3. Drift diff --git a/init-submodules b/init-submodules index 95b916b..64bb085 100755 --- a/init-submodules +++ b/init-submodules @@ -17,10 +17,10 @@ # (distributed by `config`), so `git worktree add` always checks it out — it can # therefore bring `config` itself into existence. # -# It initializes ONLY submodules that are BOTH: +# It acts ONLY on submodules that are BOTH: # -# * not yet checked out — those `git submodule status` marks with a leading `-`, -# at the commit the branch pins; and +# * not yet checked out — those `git submodule status` marks with a leading +# `-`; and # # * config-managed — `config` itself (the bootstrap target `pull` lives inside, # which carries no tracked `branch` in a consumer's `.gitmodules`), plus every @@ -28,6 +28,33 @@ # the rule `./config/pull` uses to decide what it floats, so the two scripts # can never disagree about what is shared. # +# Such a submodule is initialized and then advanced to the tip of its tracked +# branch, the way `./config/pull` floats it — NOT left at the commit the +# superproject pins. That pin is not a version anyone maintains: `.gitmodules` +# declares `ignore = all` for `.agents/shared`, which hides the submodule from +# `git status` and `git diff`, and `./config/pull` floats the on-disk copy without +# ever committing the moved gitlink. The pin therefore still holds the value it +# had on the day the submodule was adopted; an audit of all 19 Spine consumer +# repositories on 2026-10-16 found every one of them months stale. Materializing +# it hands a fresh worktree, CI checkout, or cloud session hook scripts, skills, +# and guidelines that are months out of date. +# +# A submodule may declare `branch = .`, which gitmodules(5) defines as the branch +# the superproject is on rather than a branch name. It is resolved before it +# reaches a refspec, because `git fetch origin .` is a `fatal: invalid refspec`. +# Nothing in Spine declares it today — `.agents/shared` tracks `agents`' `master` +# whatever branch the consumer is on — but it is valid in any `.gitmodules`. +# +# Neither the clone nor the float can fail the session start. With no network at +# all the clone is what fails: the submodule is left uninitialized and the +# symlinks keep dangling until a run that has a network, exactly as before this +# script floated anything. A float that fails after a successful clone — a branch +# the remote does not have, a fetch that dies midway, a local branch that is not +# a fast-forward — warns and leaves the submodule wherever the steps that did +# succeed put it: at worst the pin, a usable, if older, tree. The submodule is +# checked out by then, so later runs no longer see a `-` line for it and never +# retry the float — which is why the warning points at `./config/pull`. +# # Consumer-owned submodules (a Hugo theme, a vendored library, documentation # examples, ...) declare no tracked branch and are deliberately left untouched. # Because a `SessionStart` hook runs this script automatically on every session, @@ -35,15 +62,14 @@ # a submodule this project does not manage, on every single start. They are # skipped (noted on stderr). # -# Submodules already present are left exactly as they are, so a tree that floated -# `config` / `.agents/shared` to a branch tip via `./config/pull` is never -# silently rewound to the pin. That makes the script idempotent and safe to run on -# every session start. +# Submodules already present are left exactly as they are, neither rewound to the +# pin nor advanced. That keeps the script idempotent, keeps the runs that find +# nothing missing free of network access, and never disturbs a tree whose +# submodules someone deliberately parked on a particular commit. Run +# `./config/pull` to float those. # -# It does NOT float submodules to their branch tips — run `./config/pull` -# afterwards for that. Unlike `pull`, it depends on no pre-existing `config` -# submodule, so it can bootstrap a bare worktree where `./config/pull` does not -# yet exist. +# Unlike `pull`, this script depends on no pre-existing `config` submodule, so it +# can bootstrap a bare worktree where `./config/pull` does not yet exist. # ################################################################################ @@ -133,19 +159,29 @@ cd "$root" || exit 0 # Nothing to do in a repo without submodules. [ -f .gitmodules ] || exit 0 -# The set of config-managed submodule paths: `config` itself (handled specially — -# it carries no tracked branch, exactly as in `./config/pull`), plus every -# submodule declaring a tracked `branch` in `.gitmodules`. Mirrors `pull`'s rule. -config_managed_paths() { - printf '%s\n' 'config' +# The config-managed submodules, as `pathbranch` lines: every submodule that +# declares a tracked `branch` in `.gitmodules`, plus `config` itself — which +# carries no tracked branch in a consumer's `.gitmodules`, and which +# `./config/pull` floats to `master` in its step 1. Mirrors `pull`'s rule. The +# declared entries come first, so a `.gitmodules` that does give `config` a +# tracked branch wins over that default. +config_managed() { git config -f .gitmodules --get-regexp '^submodule\..*\.branch$' 2>/dev/null \ - | while read -r key _branch; do + | while read -r key branch; do name=${key#submodule.}; name=${name%.branch} - git config -f .gitmodules --get "submodule.$name.path" 2>/dev/null + path=$(git config -f .gitmodules --get "submodule.$name.path" 2>/dev/null) || continue + printf '%s\t%s\n' "$path" "$branch" done + printf '%s\t%s\n' 'config' 'master' } -managed=$(config_managed_paths | sort -u) +managed=$(config_managed) + +# The branch the submodule at `$1` tracks; empty when that path is not +# config-managed at all. The first match wins, as noted above. +tracked_branch() { + printf '%s\n' "$managed" | awk -F'\t' -v path="$1" '$1 == path { print $2; exit }' +} # `git submodule status` prefixes each uninitialized submodule with `-`; an # initialized one starts with a space (at the pinned commit) or `+` (ahead of it). @@ -153,13 +189,97 @@ managed=$(config_managed_paths | sort -u) # that path is config-managed. git submodule status 2>/dev/null | awk '$1 ~ /^-/ { print $2 }' | while read -r path; do [ -n "$path" ] || continue - if printf '%s\n' "$managed" | grep -qxF -- "$path"; then - echo "init-submodules: initializing '$path'" - git submodule update --init --recursive -- "$path" \ - || echo "init-submodules: WARNING — could not initialize '$path' (offline?)." >&2 - else + branch=$(tracked_branch "$path") + if [ -z "$branch" ]; then echo "init-submodules: skipping consumer-owned '$path' (not config-managed)." >&2 + continue + fi + echo "init-submodules: initializing '$path'" + git submodule update --init --recursive -- "$path" &2 + continue + } + # `.gitmodules` may declare `branch = .`, which gitmodules(5) defines as the + # branch the superproject itself is on. That is a sentinel, not a ref: passed + # through, it dies with `fatal: invalid refspec '.'`, and because the clone + # has already succeeded by then the submodule would keep the pin and never be + # retried. Resolve it to the current branch name. A detached superproject has + # no name to inherit, so the float is skipped and the pin kept — the one case + # where `./config/pull` is the only way forward, once on a branch. + if [ "$branch" = "." ]; then + # `git branch --show-current` prints the name exactly, and prints nothing + # when `HEAD` is detached. `git rev-parse --abbrev-ref HEAD` cannot stand + # in: it shortens to a *non-ambiguous* name, so a superproject carrying + # both a branch and a tag called `foo` yields `heads/foo`; the fetch + # then succeeds but the checkout fails, leaving this freshly + # initialized submodule stale and never retried. `./config/pull` + # resolves it the same way, for the same reason. + branch=$(git branch --show-current 2>/dev/null) + if [ -z "$branch" ]; then + echo "init-submodules: WARNING — '$path' tracks the superproject's" \ + "branch ('.'), which is detached here; left at the pin. Run" \ + "'./config/pull' from a branch to float it." >&2 + continue + fi fi + # What that just checked out is the superproject's pin, which nobody + # maintains (see the header). Advance the fresh checkout to the tip of its + # tracked branch, leaving it ON the branch rather than in a detached `HEAD` — + # the same three steps `./config/pull` takes in its step 2, both scripts + # naming the fetch destination and the checkout start point, for the two + # reasons below. A failure leaves the submodule wherever the steps that did + # succeed put it, so the warning promises no particular commit, as `pull`'s + # does not. + # + # The fetch names its destination. A bare `git fetch origin ` relies + # on the refspec configured for the remote to update + # `refs/remotes/origin/` opportunistically as well, which a full + # clone's does. But a submodule declaring `shallow = true` in `.gitmodules` + # is cloned single-branch (a shallow clone implies `--single-branch`), as + # `+refs/heads/:refs/remotes/origin/`, so any other tracked + # branch lands in `FETCH_HEAD` alone, `refs/remotes/origin/` never + # appears, and the checkout below fails. Spelling the refspec out makes the + # float work whatever refspec the clone carries, and the leading `+` matches + # what a default clone configures. + # + # The checkout asks whether the branch exists locally rather than letting Git + # work it out, because both answers are otherwise wrong in a reachable case. + # A fresh clone has a local branch only for the default branch, so `-b` alone + # would fail with `already exists` on the common path. And plain + # `git checkout ` is not a reliable substitute: with no local branch + # of that name it guesses one from a remote-tracking ref, considering only + # refs the remote's fetch refspec covers — so in the single-branch clone + # above it fails even though the explicit fetch did create + # `refs/remotes/origin/` — and if the submodule carries a *tag* of + # that name it quietly resolves the tag and detaches `HEAD` instead, which + # the `--ff-only` below then fast-forwards to the right commit on no branch + # at all. Naming the start point avoids both. + # + # `merge --ff-only` is kept after either path: a local branch that has + # diverged from `origin/` is reported rather than overwritten. + # Nothing here force-moves a ref except the remote-tracking ref the fetch + # owns, which a default clone force-updates anyway. + # + # It merges `refs/remotes/origin/`, not the `origin/` + # shorthand, for the reason the checkout above spells a ref out too. + # gitrevisions(7) resolves a bare name through `refs/tags/` BEFORE + # `refs/remotes/`, so a tag literally named `origin/` wins the + # shorthand — and then this merge would fast-forward the branch the + # checkout just placed exactly onto whatever that tag points at, reporting + # success. Every ref in the chain is therefore fully qualified. + echo "init-submodules: floating '$path' -> origin/$branch" + ( cd "$path" \ + && git fetch --quiet origin \ + "+refs/heads/$branch:refs/remotes/origin/$branch" \ + && if git show-ref --verify --quiet "refs/heads/$branch"; then \ + git checkout --quiet "$branch"; \ + else \ + git checkout --quiet -b "$branch" \ + "refs/remotes/origin/$branch"; \ + fi \ + && git merge --quiet --ff-only "refs/remotes/origin/$branch" ) &2 done # Route Git hooks to the shared hooks directory so the secret-scan `pre-commit`