You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #36609 (SDK compatibility handshake), which shipped MinSdkVersion.java as a manually-edited Java constant. The bump procedure documented there ("Option A": developer sets MinSdkVersion.VALUE to whichever @dotcms/client version is currently published as latest on npm at PR time) works correctly for backend-only breaking changes, but has a real gap when a breaking change also requires an SDK-side code fix: the "latest published version at PR time" necessarily predates that fix, so setting the floor to it would incorrectly mark an unfixed SDK version as "supported."
This issue proposes replacing the manual, PR-time Java-constant-edit procedure with a release-pipeline-driven mechanism, so:
Developers never write a version number by hand or guess a not-yet-existent future date.
The value only ever gets set at the moment it is actually known (when a release is cut).
Forgetting to bump it is a loud pipeline failure, not a silent gap.
Proposed solution
1. New release-pipeline input (no separate date field — reuses release_version)
Add a new workflow_dispatch boolean input to cicd_6-release.yml:
bump_min_sdk_version (boolean, default false) — "Does this release include a change that breaks SDK compatibility?"
Important design correction (found while scoping this issue): the bump must land on main, on its own — it must not be folded into cicd_comp_release-prepare-phase.yml's existing automated commit (the one that also touches .mvn/maven.config and LICENSE). That commit only ever lives on the disposable release-${release_version} branch, which is never merged back into main (verified: no step anywhere in the release pipeline merges a release branch back to main). That's fine for LICENSE and maven.config, because update-license-date.shrecomputes the Change Date fresh from the current date on every run — it never depends on prior state, so it doesn't matter that it never reaches main. MinSdkVersion.VALUE is the opposite: it's a ratchet that must persist across releases. If its bump only ever lived on the release branch, the very next release-prepare run would start again from main's stale value, silently undoing the bump.
This also means timing matters, not just placement: the mechanism to update main must only fire after the release has actually succeeded (build + deployment both green), not eagerly in release-prepare. If release-prepare bumped main immediately and the release then failed downstream, main would advertise a stricter MIN_SDK_VERSION for a dotCMS version that was never actually shipped — breaking currently-valid customer SDK installs for no reason. Concretely: this needs to be a new step/job gated on needs: [release-prepare, build, deployment] with if: success() (likely in cicd_6-release.yml's release job, or a new job right after it).
Confirmed (not just suspected): main cannot be pushed to directly — this must go through a PR. Checked the precedent this repo already used for an almost identical problem: the now-retired cicd_manual-release-sdks.yml had to persist a version bump (core-web/libs/sdk/VERSION) onto main after a release, and its solution was a dedicated step, Open post-release PR to bump VERSION on main — it committed to its own branch, force-pushed that branch, then ran gh pr create --base main (reusing an existing PR via gh pr list --head ... --json url if a previous run had already opened one still pending). Verified via the actual resulting PR (#36563, "chore(sdk): bump SDK version to 1.7.1 after 1.7.0 release"): opened by github-actions[bot], but merged by a human, not auto-merged — confirmed no auto-merge mechanism exists anywhere in this repo's workflows.
This means our new mechanism should follow the exact same shape:
Create a branch, commit the MinSdkVersion.java bump there, push it, and gh pr create --base main (with the same dedupe-against-an-existing-open-PR check as the retired workflow, in case a previous release run already opened one that's still unmerged).
Because a human has to merge this PR, an open-but-unmerged bump PR is itself the same kind of silent gap this issue is trying to eliminate — if nobody notices it, the ratchet never actually advances even though the release believes it handled it. So this step must also post a Slack notification (reusing the existing notify-slack action already used elsewhere in these pipelines) explicitly asking someone to review and merge the PR, not just open it quietly.
Idempotency still holds the same way as before: nothing lands on main until a human merges the PR, so a failed/retried release before that point leaves main untouched — there's nothing to roll back. The new step should still no-op (skip opening a duplicate PR) if MinSdkVersion.VALUE on main already matches this release's version, or if a PR bumping to this same version is already open.
No separate free-text version field is needed regardless: MIN_SDK_VERSION, when it needs to move, is always equal to the release doing the bumping — never a separately-typed, potentially-mistyped, or future-guessed value.
Add a new workflow, ai_claude-sdk-breaking-change.yml, structurally mirroring the existing ai_claude-rollback-safety.yml:
Same org-membership security gate.
Same "human override" escape hatch: a Human: SDK Breaking Change / Human: Not SDK Breaking label on the PR skips the AI evaluation.
Same stale-label-clearing preflight (re-evaluates fresh on every push).
Calls the same shared reusable workflow (dotCMS/ai-workflows/.github/workflows/claude-orchestrator.yml@v3) with a new prompt.
The prompt evaluates the PR diff against a new reference document, docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md (to be authored as part of this issue, modeled on the existing docs/core/ROLLBACK_UNSAFE_CATEGORIES.md), listing concrete categories of SDK-breaking changes (e.g. removed/renamed GraphQL field the SDK depends on, changed postMessage editor protocol shape, changed REST response shape the SDK's TS types model, etc.).
Applies label AI: SDK Breaking Change when a match is found (with an explanatory PR comment, same format as the rollback-safety check), or no label / a "clean" label otherwise.
This mechanism is entirely decoupled from the release pipeline timing change above — it runs per-PR, independent of when/whether a release is ever cut.
Important: treat the AI's label as a strong-assist signal for human PR review, not an infallible autopilot decision — semantic "does this break the SDK contract" judgment is inherently less mechanical than the rollback-safety categories (DB migrations, ES mappings, etc.), so false negatives are a real risk the human reviewer should stay alert to.
3. Release-time validation (closes the "forgot to check" gap)
Add a validation step to the release pipeline (likely in cicd_comp_release-prepare-phase.yml or a dedicated step in cicd_6-release.yml) that:
Queries merged PRs between the previous release tag and the current release commit (the same kind of tag-range query the existing .github/scripts/release-qa-status script already does for the QA-coverage Slack warning).
Checks whether any of those PRs carry the AI: SDK Breaking Change (or human-confirmed) label.
If such a PR exists and bump_min_sdk_version was left false for this run, fail the release explicitly with a clear error message (mirroring the existing strict, no-silent-fallthrough validation pattern already used for the skip_latest input in cicd_6-release.yml's verify-branch job).
Unlike the actual MinSdkVersion.java bump, this validation only reads PR/label state and doesn't mutate anything — it can safely stay early in the pipeline (e.g. in release-prepare or verify-branch) to fail fast, before spending a full build+deployment cycle on a misconfigured release.
.github/workflows/ai_claude-rollback-safety.yml — the mechanism this issue's AI-detection step is modeled on
docs/core/ROLLBACK_UNSAFE_CATEGORIES.md — the reference-doc pattern to follow for the new SDK-breaking-change categories doc
.github/scripts/release-qa-status/ — existing precedent for querying merged PRs/labels across a release tag range
Retired cicd_manual-release-sdks.yml's "Open post-release PR to bump VERSION on main" step (and its resulting PR chore(sdk): bump SDK version to 1.7.1 after 1.7.0 release #36563) — the precedent confirming main requires a PR (no direct push, no auto-merge) for this exact kind of post-release persistent-state bump
Acceptance Criteria
cicd_6-release.yml has a new bump_min_sdk_version boolean input (default false)
When true, a step gated on the release having fully succeeded (build + deployment both green) opens a PR against main with the MinSdkVersion.javaVALUE bump — not folded into release-prepare's release-branch commit, and not a direct push
That step posts a Slack notification asking a human to review and merge the bump PR (reusing the existing notify-slack action)
The step is idempotent: it reuses an already-open bump PR from a previous run instead of opening a duplicate, and no-ops entirely if main's MinSdkVersion.VALUE already matches this release's version
If the release fails before reaching this step, main is left completely untouched with respect to MinSdkVersion.VALUE (nothing to roll back)
New workflow ai_claude-sdk-breaking-change.yml exists, mirroring ai_claude-rollback-safety.yml's security gate, human-override labels, and stale-label-clearing preflight
New reference doc docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md defines concrete, evaluable categories of SDK-breaking changes
AI workflow applies AI: SDK Breaking Change label (with explanatory comment) when a match is found against the reference doc
Release pipeline fails explicitly (not silently), early (before build/deployment), if a merged PR since the last release tag carries the breaking-change label but bump_min_sdk_version was left false
Purely a CI/CD process-automation improvement for maintaining MIN_SDK_VERSION going forward — does not touch the already-shipped, already-tested runtime handshake feature from #36609.
Description
Follow-up to #36609 (SDK compatibility handshake), which shipped
MinSdkVersion.javaas a manually-edited Java constant. The bump procedure documented there ("Option A": developer setsMinSdkVersion.VALUEto whichever@dotcms/clientversion is currently published aslateston npm at PR time) works correctly for backend-only breaking changes, but has a real gap when a breaking change also requires an SDK-side code fix: the "latest published version at PR time" necessarily predates that fix, so setting the floor to it would incorrectly mark an unfixed SDK version as "supported."This issue proposes replacing the manual, PR-time Java-constant-edit procedure with a release-pipeline-driven mechanism, so:
Proposed solution
1. New release-pipeline input (no separate date field — reuses
release_version)Add a new
workflow_dispatchboolean input tocicd_6-release.yml:bump_min_sdk_version(boolean, defaultfalse) — "Does this release include a change that breaks SDK compatibility?"Important design correction (found while scoping this issue): the bump must land on
main, on its own — it must not be folded intocicd_comp_release-prepare-phase.yml's existing automated commit (the one that also touches.mvn/maven.configandLICENSE). That commit only ever lives on the disposablerelease-${release_version}branch, which is never merged back intomain(verified: no step anywhere in the release pipeline merges a release branch back tomain). That's fine forLICENSEandmaven.config, becauseupdate-license-date.shrecomputes the Change Date fresh from the current date on every run — it never depends on prior state, so it doesn't matter that it never reachesmain.MinSdkVersion.VALUEis the opposite: it's a ratchet that must persist across releases. If its bump only ever lived on the release branch, the very next release-prepare run would start again frommain's stale value, silently undoing the bump.This also means timing matters, not just placement: the mechanism to update
mainmust only fire after the release has actually succeeded (build+deploymentboth green), not eagerly inrelease-prepare. Ifrelease-preparebumpedmainimmediately and the release then failed downstream,mainwould advertise a stricterMIN_SDK_VERSIONfor a dotCMS version that was never actually shipped — breaking currently-valid customer SDK installs for no reason. Concretely: this needs to be a new step/job gated onneeds: [release-prepare, build, deployment]withif: success()(likely incicd_6-release.yml'sreleasejob, or a new job right after it).Confirmed (not just suspected):
maincannot be pushed to directly — this must go through a PR. Checked the precedent this repo already used for an almost identical problem: the now-retiredcicd_manual-release-sdks.ymlhad to persist a version bump (core-web/libs/sdk/VERSION) ontomainafter a release, and its solution was a dedicated step,Open post-release PR to bump VERSION on main— it committed to its own branch, force-pushed that branch, then rangh pr create --base main(reusing an existing PR viagh pr list --head ... --json urlif a previous run had already opened one still pending). Verified via the actual resulting PR (#36563, "chore(sdk): bump SDK version to 1.7.1 after 1.7.0 release"): opened bygithub-actions[bot], but merged by a human, not auto-merged — confirmed no auto-merge mechanism exists anywhere in this repo's workflows.This means our new mechanism should follow the exact same shape:
MinSdkVersion.javabump there, push it, andgh pr create --base main(with the same dedupe-against-an-existing-open-PR check as the retired workflow, in case a previous release run already opened one that's still unmerged).notify-slackaction already used elsewhere in these pipelines) explicitly asking someone to review and merge the PR, not just open it quietly.mainuntil a human merges the PR, so a failed/retried release before that point leavesmainuntouched — there's nothing to roll back. The new step should still no-op (skip opening a duplicate PR) ifMinSdkVersion.VALUEonmainalready matches this release's version, or if a PR bumping to this same version is already open.No separate free-text version field is needed regardless:
MIN_SDK_VERSION, when it needs to move, is always equal to the release doing the bumping — never a separately-typed, potentially-mistyped, or future-guessed value.2. AI-based label detection (safety net, mirrors the existing rollback-safety mechanism)
Add a new workflow,
ai_claude-sdk-breaking-change.yml, structurally mirroring the existingai_claude-rollback-safety.yml:Human: SDK Breaking Change/Human: Not SDK Breakinglabel on the PR skips the AI evaluation.dotCMS/ai-workflows/.github/workflows/claude-orchestrator.yml@v3) with a new prompt.docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md(to be authored as part of this issue, modeled on the existingdocs/core/ROLLBACK_UNSAFE_CATEGORIES.md), listing concrete categories of SDK-breaking changes (e.g. removed/renamed GraphQL field the SDK depends on, changedpostMessageeditor protocol shape, changed REST response shape the SDK's TS types model, etc.).AI: SDK Breaking Changewhen a match is found (with an explanatory PR comment, same format as the rollback-safety check), or no label / a "clean" label otherwise.This mechanism is entirely decoupled from the release pipeline timing change above — it runs per-PR, independent of when/whether a release is ever cut.
Important: treat the AI's label as a strong-assist signal for human PR review, not an infallible autopilot decision — semantic "does this break the SDK contract" judgment is inherently less mechanical than the rollback-safety categories (DB migrations, ES mappings, etc.), so false negatives are a real risk the human reviewer should stay alert to.
3. Release-time validation (closes the "forgot to check" gap)
Add a validation step to the release pipeline (likely in
cicd_comp_release-prepare-phase.ymlor a dedicated step incicd_6-release.yml) that:.github/scripts/release-qa-statusscript already does for the QA-coverage Slack warning).AI: SDK Breaking Change(or human-confirmed) label.bump_min_sdk_versionwas leftfalsefor this run, fail the release explicitly with a clear error message (mirroring the existing strict, no-silent-fallthrough validation pattern already used for theskip_latestinput incicd_6-release.yml'sverify-branchjob).Unlike the actual
MinSdkVersion.javabump, this validation only reads PR/label state and doesn't mutate anything — it can safely stay early in the pipeline (e.g. inrelease-prepareorverify-branch) to fail fast, before spending a full build+deployment cycle on a misconfigured release.Out of scope
SdkVersionWebInterceptor) — that shipped in SDK compatibility handshake: server advertises version + minimum supported SDK, SDKs warn on mismatch #36609/PR feat(sdk): Implement SDK compatibility handshake server advertises version + minimum supported SDK, SDKs warn on mismatch #36678 and does not change here; this issue is purely about howMinSdkVersion.VALUEgets updated, not the mechanism that reads/compares it.MIN_SDK_VERSIONpolicy.References
.github/workflows/ai_claude-rollback-safety.yml— the mechanism this issue's AI-detection step is modeled ondocs/core/ROLLBACK_UNSAFE_CATEGORIES.md— the reference-doc pattern to follow for the new SDK-breaking-change categories doc.github/scripts/release-qa-status/— existing precedent for querying merged PRs/labels across a release tag rangecicd_manual-release-sdks.yml's "Open post-release PR to bump VERSION on main" step (and its resulting PR chore(sdk): bump SDK version to 1.7.1 after 1.7.0 release #36563) — the precedent confirmingmainrequires a PR (no direct push, no auto-merge) for this exact kind of post-release persistent-state bumpAcceptance Criteria
cicd_6-release.ymlhas a newbump_min_sdk_versionboolean input (defaultfalse)true, a step gated on the release having fully succeeded (build+deploymentboth green) opens a PR againstmainwith theMinSdkVersion.javaVALUEbump — not folded intorelease-prepare's release-branch commit, and not a direct pushnotify-slackaction)main'sMinSdkVersion.VALUEalready matches this release's versionmainis left completely untouched with respect toMinSdkVersion.VALUE(nothing to roll back)ai_claude-sdk-breaking-change.ymlexists, mirroringai_claude-rollback-safety.yml's security gate, human-override labels, and stale-label-clearing preflightdocs/core/SDK_BREAKING_CHANGE_CATEGORIES.mddefines concrete, evaluable categories of SDK-breaking changesAI: SDK Breaking Changelabel (with explanatory comment) when a match is found against the reference docbump_min_sdk_versionwas leftfalseMinSdkVersion.java's Javadoc (from SDK compatibility handshake: server advertises version + minimum supported SDK, SDKs warn on mismatch #36609) is updated to describe this pipeline-driven procedure (bumped via an automated PR againstmain, opened after a successful release, merged by a human) instead of the superseded manual "Option A" (developer edits the constant directly in their PR)Priority
High
Additional Context
Purely a CI/CD process-automation improvement for maintaining
MIN_SDK_VERSIONgoing forward — does not touch the already-shipped, already-tested runtime handshake feature from #36609.