feat(api): report promotion window state on Stage status - #6772
Merged
Conversation
✅ Deploy Preview for docs-kargo-io ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6772 +/- ##
==========================================
+ Coverage 30.83% 30.95% +0.11%
==========================================
Files 824 825 +1
Lines 80467 98378 +17911
==========================================
+ Hits 24815 30456 +5641
- Misses 54196 66466 +12270
Partials 1456 1456 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
fuskovic
reviewed
Aug 7, 2026
| // NextCloseReason explains in human-readable terms why promotion will be forbidden | ||
| // at NextClose, naming the freeze responsible where there is one. | ||
| // It is set whenever NextClose is set. | ||
| NextCloseReason string `json:"nextCloseReason,omitempty"` |
Member
There was a problem hiding this comment.
Should we add an +optional marker here?
Suggested change
| NextCloseReason string `json:"nextCloseReason,omitempty"` | |
| // | |
| // +optional | |
| NextCloseReason string `json:"nextCloseReason,omitempty"` |
fuskovic
approved these changes
Aug 7, 2026
fuskovic
left a comment
Member
There was a problem hiding this comment.
One small nit other than that LGTM
Contributor
Author
|
@rpelczar please take a look since you were going to work on the UI part of it. |
A Stage gated by a promotion window offers no way to ask whether promotion is currently possible. The only answer available is the rejection returned when someone tries and fails, which means a user interface can explain a freeze only after a user has run into it. PromotionScheduleStatus reports the evaluated union of every window matching the Stage: whether promotion is currently forbidden, why, and when that is next expected to change in either direction. NextClose lets a client warn of an approaching freeze while promotion is still allowed, rather than only describing one already in effect. It follows AutoPromotionEnabled, which is likewise a status field derived from ProjectConfig so that clients need not recompute policy for themselves. The field is advisory and says so. It is only as current as the last reconciliation of the Stage, so admission stays the sole arbiter of whether a Promotion is permitted; this exists to explain a freeze, not to authorize anything. Clients that treat it as authorization will eventually act on a stale value. Reason describes the freeze being reported rather than the current state alone, and Closed says which of the two: while Closed it explains the freeze in effect, and alongside NextClose it explains the freeze that is coming. One field covers both because in both cases the question a reader has is the same -- which freeze is this, and why. NextOpen is optional even while Closed is true. A schedule need not have any future opening -- a one-shot Allow window that has already elapsed forbids promotion for good -- so a contract requiring the timestamp would be one the very first implementation breaks. Reason is therefore the field guaranteed to explain a freeze in effect, and the godoc tells clients to render it and treat a missing NextOpen as "frozen, with no known end". Leaving the two unlinked also lets an implementation express an indefinite freeze honestly rather than inventing a reopening it cannot predict. As with PromotionWindow itself, evaluation is Enterprise-only and nothing in OSS writes this field; OSS carries the API for compatibility, as it does for other Enterprise-only fields. Signed-off-by: Eron Wright <eron.wright@akuity.io>
…lds less confusing. - `promotionSchedule` -> `promotionWindowStatus` - unify naming with `promotionWindows` - `closed` is non-optional if `promotionWindowStatus` exists for clarity - `reason` should only describe a reason for closed window, `nextCloseReason` is added to describe a reason for `nextClose` - this is to prevent people from thinking that `reason` describes a reason why it's open. Signed-off-by: Daniil Fedotov <daniil.fedotov@akuity.io>
Signed-off-by: Daniil Fedotov <daniil.fedotov@akuity.io>
hairyhum
force-pushed
the
EronWright/promotion-schedule-status
branch
from
August 7, 2026 18:44
4cfce0d to
1752c60
Compare
hairyhum
enabled auto-merge
August 10, 2026 15:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Supersedes #6737
Some name replacements compared to #6737:
promotionSchedule->promotionWindowStatus- unify naming withpromotionWindowsclosedis non-optional ifpromotionWindowStatusexists for clarityreasonshould only describe a reason for closed window,nextCloseReasonis added to describe a reason fornextClose- this is to prevent people from thinking thatreasondescribes a reason why it's open.Adds PromotionWindowStatus to StageStatus, reporting whether promotion windows (#6710) currently permit promotion of a Stage.
Variations a client will observe
No windows gate the Stage. The common case — the field is absent entirely, not an empty object.
Frozen, reopening known.
Frozen, no reopening known.
nextOpenis omitted. Here a one-shotAllowwindow has elapsed, so promotion is forbidden for good; an implementation may also report an indefinite freeze this way.Promotion allowed, freeze approaching.
closedisfalse.nextCloseReasondescribes the freeze that is coming, so a warning can name it.Promotion allowed, nothing scheduled ahead. A one-shot
Denywindow that has elapsed leaves no boundary to report. This is identical topromotionSchedulefield being absentContract
closedindicates whether promotions are blockedreasondescribes whyclosedistrue, should only be non-empty ifclosedistruenextOpenis optional even whenclosedis true. A schedule need not ever reopen, so requiring it would be a contract the first implementation breaks. No CEL validation links the two. Treat its absence as "frozen, with no known end" and renderreason.nextCloseis meaningful only while promotion is allowed, and is likewise optional.nextCloseReasondescribes a reason for expected next closure, should only be non-empty ifnextCloseis present.nextOpen/nextCloseappears: only the boundary ahead is reported.AutoPromotionEnabledis the precedent: likewise a status field derived fromProjectConfigso clients need not recompute policy.A note on wording
The messages say "promotion freeze" rather than "Deny promotion window", and "promotion window" rather than "Allow promotion window".
kind: Allow|Denyremains the configuration vocabulary, where an explicit pair is unambiguous; it just reads poorly in a sentence, and these strings are shown to people.Testing
API-only; no behavior change. Full codegen (CRD, deepcopy, OpenAPI, generated clients, UI schema and models).
apiand root modules build and their tests pass. The Stage CRD gains one optional object understatus, so the change is additive for existing resources.