Changelog¶
All notable changes to kardinal-promoter are documented here. Format follows Keep a Changelog. Versioning follows Semantic Versioning.
Unreleased¶
UI accessibility compliance, DX polish, keyboard shortcuts
Added¶
kardinal-agentstandalone binary — separate binary for spoke-cluster distributed mode (cmd/kardinal-agent/); runs only the PromotionStep reconciler for a specific shard, without Bundle, Pipeline, PolicyGate, UI, or webhook components. Required flag:--shard. Defaults to ports :8085/:8086 to avoid collision with the controller. (#886)kubectl getprinter columns on Bundle and PromotionStep CRDs —kubectl get bundlenow shows Type, Pipeline, Phase, Age;kubectl get promotionstep(shortname:ps) now shows Pipeline, Env, Bundle, State, Age. No morekubectl describeto find which pipeline a step belongs to. (#903)- WaitingForMerge timeout —
environment.waitForMergeTimeouton Pipeline environments (e.g.waitForMergeTimeout: "24h") causes a PromotionStep stuck waiting for a PR reviewer to transition toFailedafter the configured duration. No timeout by default (no behavior change for existing pipelines). Closes production-blocker: abandoned PR reviews no longer stall pipelines indefinitely. (#906) - Shell completion —
kardinal completion bash|zsh|fish|powershell(#731) - Color output for
kardinal explain—--colorflag, auto-detected TTY; Pass=green, Block=red, Pending=yellow (#730) - Dark/light mode — system-aware theme with manual toggle in the embedded UI (#734)
- Prometheus metrics —
kardinal_bundles_total{phase},kardinal_steps_total{type,result},kardinal_gate_evaluations_total{result},kardinal_pr_duration_secondshistogram emitted from reconcilers (#726) kardinal validate— validate Pipeline and PolicyGate YAML files offline; checks CEL syntax and schema (#713)kardinal status— show controller health, CRD versions, and resource summary (#715)- Improved explain error —
kardinal explain <pipeline> --env <bogus>now lists available environments (#717) - Improved circular dependency error — cycle path displayed in full (#711)
--dry-runforkardinal create bundle— print what would be created without submitting; useful for CI validation (#741)- URL routing in embedded UI — selecting a pipeline, node, or bundle diff panel updates the URL hash fragment; page reload and back/forward navigation restore selection (#742)
- Keyboard shortcuts in embedded UI —
?opens help panel,rrefreshes,Esccloses panels (#750) - Error boundaries on async UI components — DAGView, PipelineList, NodeDetail, BundleTimeline show a user-friendly message + Retry on render error (#755)
- WCAG 2.1 AA accessibility enforcement — axe-core Playwright check in E2E test suite (Journey 009); all structural violations resolved (#756, #759, #760)
- Copy-to-clipboard on pipeline names and bundle hashes — click to copy pipeline name, bundle name, or commit SHA in the embedded UI (#764)
- Stale data indicator escalation — red + pulse animation when dashboard data is >30s old; screen reader announcement via
aria-live(#767) - Skeleton loading states — NodeDetail step details, BundleTimeline chips, and PolicyGatesPanel now show animated shimmer placeholders instead of blank panels while data loads (#784)
/keyboard shortcut to focus pipeline search — pressing/anywhere (except when an input is focused) moves keyboard focus to the pipeline filter input;Escinside the filter clears and blurs;/listed in the?shortcut help modal (#800)- Responsive layout at 1280px — Journey 010 Playwright test formalizes the no-overflow guarantee at 1280×800 viewport (#806)
- Virtual scrolling for pipeline list —
@tanstack/react-virtualused for flat lists exceeding 50 entries; multi-namespace grouped display falls back to normal rendering (#817) - Full adapter demo coverage — examples, unit tests, and
docs/demo-validation.mdfor all 5 health adapters: resource, argocd, flux, argoRollouts, flagger (#821) kardinal delete bundle <name>— new CLI command to explicitly delete a Bundle by name, cancelling any in-progress promotion (#851)
Changed¶
- UI CSS theme tokens — migrated 206 hardcoded hex color values to CSS custom properties (
--color-*tokens); consistent theming across dark/light mode (#738) - UI WCAG color contrast — all theme colors now meet WCAG 2.1 AA 4.5:1 minimum contrast ratio in both dark and light modes (#760)
- krocodile upgraded to
3376810— correctness fix: propagation trigger on self-state refresh. Without this, UAT PromotionStep was never created after test reached Verified. Also includes NodeState sync guard and ForEach typed binding (#789) - krocodile upgraded to
d6cbc54— additive forEach incremental diff optimization (O(K) vs O(N) rehash), WatchManager canonical Kind caching fix. No breaking changes (#803)
Fixed¶
- Demo Validate nightly workflow — missing
issues: writepermission causedGraphQL: Resource not accessible by integration (addComment)on every scheduled run; fixed and improved to post to the current daily report issue instead of hard-coded #1 (#801) - UAT never starting after test Verified — krocodile Path 2 (self-state refresh) now correctly marks dependents as
propagationTriggered; UAT PromotionStep is created once test PS writesstatus.state=Verified(#789) - AbortedByAlarm / RollingBack cycling — PromotionStep reconciler now handles
AbortedByAlarmandRollingBackas explicit terminal/managed cases, preventing fall-through todefaultwhich incorrectly reset state to Pending (#789)
v0.8.1 — 2026-04-17¶
Security release: supply chain hardening — trivy, cosign, SBOM, SLSA, krocodile scan
v0.8.0 — 2026-04-17¶
Audit log, SCM circuit breaker, Bundle Watch node, Graph-first cleanup, DX improvements
v0.7.0 — 2026-04-17¶
WatchKind O(1) health checks, krocodile 81c5a03 upgrade, reactive PromotionStep reconciler, graph-first cleanup
Added¶
- WatchKind health nodes —
health.labelSelectoron Pipeline environments switches from Watch (O(n) full list per event) to WatchKind (O(1) incremental cache). Requires krocodile745998f+ (#652) - Kargo migration guide — concept mapping, side-by-side Pipeline vs Kargo YAML, 7-step migration walkthrough in
docs/guides/(#640) - Operations runbook expanded — PolicyGate debugging, SCM failure modes, RBAC issues, krocodile restarts, performance tuning added (#639)
- Bundle image diff in NodeDetail — UI compares the current bundle's image against the previous bundle for that environment; closes a Kargo parity gap (#638)
- Per-step progress observability —
PromotionStep.status.steps[]exposes each step with individual state, start time, and duration (#630) kardinal get pipelines --watch— real-time promotion progress with live table refresh (#629)- PrometheusRule CRD — 6 pre-built alerting rules in Helm chart: promotion stuck, high rollback rate, policy gate blocked, SCM errors (#621)
- UI: conditions summary and reason — NodeDetail shows Kubernetes Conditions table with reason column; improved empty-state onboarding (#529, #530)
- UI: Kubernetes events stream — timestamped event history per PromotionStep in NodeDetail (#560)
- UI: cross-environment error aggregation — groups PromotionStep failures by type across environments; shows affected count (#564)
- krocodile upgraded to
745998f— Decorator bootstrap primitive, Definition compile-time type inference, forEach array format support, DAG finalizer guard for non-resource nodes (#614)
Fixed¶
- Bundle reconciler watches Pipeline changes — Graph is regenerated when Pipeline spec changes (new environments, updated policyNamespaces, changed git config). Previously Pipeline changes were invisible to in-flight Bundles (#634)
- Subscription deduplication under HA — uses label selector (
kardinal.io/source-digest) instead of status field comparison; safe under concurrent reconciles and multiple controller replicas (#636) - CEL documentation accuracy — corrected false claims about
pkg/cel/NewCELEnvironment()(does not exist) andschedule.*(map variable, not CEL library function) in design docs and code comments (#631) - Shell completion — bash, zsh, fish, and PowerShell completion scripts via
kardinal completion <shell>(#606) kardinal doctor— pre-flight cluster health check: validates CRD installation, krocodile, RBAC, and GitHub token before first use (#607)- Graceful shutdown — controller drains in-flight reconcile loops on SIGTERM; no promotion steps interrupted by pod restarts (#605)
- PodDisruptionBudget + topology spread — minAvailable: 1 PDB and
topologySpreadConstraintsin Helm chart for HA deployments (#598) - krocodile bundled in Helm chart — single
helm installnow installs both kardinal-promoter and the krocodile Graph controller; no separatehack/install-krocodile.shstep needed (#590) - Library-based git operations — replaced
exec.Command("git")withgo-gitlibrary (#517). Controller no longer requires agitbinary. Improves portability (distroless images) and performance. - Controller
/tmpmount —emptyDirvolume added for git-clone withreadOnlyRootFilesystem: true(#609) policy simulatenow searches all namespaces — org-level gates inplatform-policieswere never foundpkg/celstandalone CEL evaluator eliminated — evaluation moved inline to PolicyGate reconciler- Rollback PR title and body now include rollback notice and the
kardinal/rollbacklabel
v0.6.0 — 2026-04-14¶
Live-cluster validation infrastructure, J7 multi-tenant self-service, OCI/Git source watchers, pipeline deployment metrics
Added¶
- Multi-tenant self-service (J7) — ApplicationSet + Pipeline template bootstrap; team onboarding via Git directory; org PolicyGates automatically inherited (#489)
- OCI + Git source watchers —
OCIWatcherandGitWatcherSubscription reconcilers poll registries and Git branches, creating Bundles on new images/commits (#491, #493) - Pipeline deployment metrics —
Pipeline.status.deploymentMetricsaggregated byPipelineReconciler:rolloutsLast30Days,p50CommitToProdMinutes,p90CommitToProdMinutes,autoRollbackRate(#498) changewindow.isAllowed()/changewindow.isBlocked()CEL functions — named-argument helpers for ChangeWindow gates (#506)- krocodile upgraded to
948ad6c— DNS-1123 node ID validation, drift timers (30 min), propagation hash includespropagateWhenstate - Cardinal logo — added across docs site, UI sidebar, and README
Fixed¶
kardinal-promotercontroller image rebuilt correctly when Graph CR is deleted externally (#490)- PDCA live-cluster validation workflow: fixed pipeline name mismatch, missing
platform-policiesnamespace, missing controller install step (#514) - CI:
enforce_admins: trueon branch protection; 8 required status checks; concurrency guards on Docs and E2E workflows (#513)
v0.5.0 — 2026-04-13¶
Pipeline Expressiveness (K-Series), Enterprise UI Control Plane, krocodile upgrade
Added¶
- K-01: Contiguous healthy soak —
bake.minutes+bake.policy: reset-on-alarmon environment spec;BakeElapsedMinutesandBakeResetstracked in PromotionStep status - K-02: Pre-deploy gate type —
when: pre-deployon PolicyGate spec; blocks PromotionStep inWaitingstate beforegit-clonestarts - K-03: Auto-rollback with ABORT vs ROLLBACK distinction —
onHealthFailure: rollback | abort | noneper environment - K-04: ChangeWindow CRD — blackout and recurring allowed-hours windows;
changewindow["name"]CEL function evaluates totruewhen the window is active/blocking - K-05: Bundle.status.metrics — commitToFirstStageMinutes, commitToProductionMinutes, bakeResets, operatorInterventions;
kardinal metricsCLI command - K-06: Wave topology —
wave: Nfield on environment spec; Wave N automatically depends on all Wave N-1 stages - K-07: Integration test step — built-in
integration-teststep runs a Kubernetes Job as part of the promotion sequence - K-08: PR review gate —
bundle.pr["staging"].isApprovedand.approvalCountin CEL context via PRStatus CRD - K-09:
kardinal overridewith audit record — emergency gate override with mandatory reason + time limit; override record in Bundle status and PR evidence body - K-10: Cross-stage history CEL —
upstream.<env>.soakMinutes,.recentSuccessCount,.recentFailureCount,.lastPromotedAtin gate expressions - UI control plane — all 7 UI issues shipped (#462–#468): fleet health dashboard, pipeline ops view, per-stage bake countdown, in-UI actions (pause/resume/rollback/override), release metrics bar, bundle timeline, policy gate detail panel
Fixed¶
- krocodile upgraded to
948ad6c— DNS-1123 node ID validation, drift timers, propagation hash improvements changewindow.isAllowed()/changewindow.isBlocked()CEL helpers added alongside the map-style access
v0.4.0 — 2026-04-12¶
Distributed Mode, Argo Rollouts delegation, graph purity, K-series features
Added¶
- Distributed mode —
--shardflag routes PromotionSteps to matching shard agents; supports multi-cluster deployments where each spoke cluster runs its own agent - Argo Rollouts delivery delegation —
delivery.delegate: argo-rolloutsin Pipeline env spec hands off rollout progression to an existingRolloutresource - GitLab + Forgejo/Gitea SCM providers —
scm.provider: gitlabandscm.provider: forgejoin Pipeline spec - PRStatus CRD — makes PR merge/close signal observable by the Graph (eliminates 6 GitHub API call paths from the reconciler hot path)
- RollbackPolicy CRD — auto-rollback threshold comparison moved to dedicated reconciler
- Graph purity milestone — all 41 krocodile-independent logic leaks eliminated (see
docs/design/11-graph-purity-tech-debt.md) - K-01–K-11 — all Pipeline Expressiveness features (see v0.5.0 above for full list; initial implementation in this release)
Fixed¶
- Pause enforcement via
bundle.status.paused(eliminates in-memory pause state) - Step cleanup on pipeline deletion no longer leaves orphaned PromotionSteps
- PolicyGate re-evaluation after TTL now fires correctly when graph resumes
v0.3.0 — 2026-04-12¶
Observability: embedded UI, PR evidence, GitHub Actions
Added¶
- Embedded React UI — promotion DAG visualization with 6-state health chips, CEL expression display, live polling with staleness indicator, blocked-gate banner
- PR evidence body — structured markdown in every prod PR: image digest, CI run link, gate results, upstream soak time
- kardinal diff —
kardinal diff <bundle-a> <bundle-b>shows artifact delta - kardinal approve — approve a Bundle bypassing upstream gate requirements
- kardinal metrics — DORA-style promotion metrics (deployment frequency, lead time, fail rate)
- kardinal refresh / dashboard / logs — operational CLI commands
- History command —
kardinal history <pipeline>shows previous promotions
Fixed¶
- DAG graph deduplicates gate nodes (was showing duplicate PolicyGate cards)
- Pipeline phase uses
status.phasenot condition reason for display - Explain command deduplicates gate output when multiple instances match
v0.2.1 — 2026-04-12¶
Graph Purity: all krocodile-independent logic leaks eliminated
Added¶
- PRStatus CRD — replaces in-reconciler GitHub API polling for PR state
- RollbackPolicy CRD — moves auto-rollback threshold logic out of PromotionStepReconciler
- ScheduleClock CRD — writes
status.tickon a configurable interval to drive time-based policy gate re-evaluation via real Kubernetes watch events; replaces thectrl.Result{RequeueAfter}timer loop pattern
Fixed¶
time.Now()calls moved outside reconciler hot paths into CRD status writes- Cross-CRD status mutations eliminated — each reconciler writes only to its own CRD
exec.Command()in reconciler replaced with library call
v0.2.0 — 2026-04-11¶
Workshop 1 Complete — first end-to-end validated release
Milestone¶
kardinal-promoter now executes the AWS Platform Engineering on EKS workshop end-to-end on a live kind cluster.
Added¶
- MetricCheck CRD — Prometheus-backed policy gates: block promotions when error rate > threshold
- Custom promotion steps — HTTP webhook steps for extensible promotion workflows
- Auto-rollback — configurable failure threshold triggers rollback PR after N consecutive health failures
- Pause/resume —
kardinal pause/resume <pipeline>halts in-flight promotions - Policy simulate —
kardinal policy simulateevaluates gates without creating a Bundle - Policy test —
kardinal policy testvalidates CEL syntax offline - Policy list — lists active PolicyGates scoped to a pipeline/environment
- Promote command —
kardinal promotecreates a Bundle from the last verified image - Config Bundle type — promotes Git commit SHAs through the same pipeline as image Bundles
- Rendered manifests step —
pre-renderstrategy generates environment-specific YAML
Fixed¶
- kind E2E infrastructure (
make setup-e2e-env) sets up krocodile + ArgoCD + test/uat/prod namespaces kardinal get pipelinesshows per-environment status columnskardinal explainshows active PolicyGates with CEL expression and current value
v0.1.0 — 2026-04-11¶
Foundation: CRDs, Controller, Graph Integration, PolicyGate
Added¶
- Go module scaffold — directory layout, Makefile, CI pipeline (build/lint/test/vet)
- CRD types — Pipeline, Bundle, PolicyGate, PromotionStep (kubebuilder markers, deep copy, validation)
- Controller manager — BundleReconciler, PipelineReconciler, PromotionStepReconciler, PolicyGateReconciler
- Helm chart — controller deployment, RBAC, CRDs packaged for OCI registry
- Graph integration — kro Graph builder and translator: Pipeline → Graph spec
- PolicyGate CEL evaluator —
!schedule.isWeekend,upstream.uat.soakMinutes >= 30, kro CEL library - SCM provider — GitHub: push branch, open PR, detect merge, post comments
- Health adapters — Kubernetes Deployment readiness, ArgoCD Application sync, Flux Kustomization
- Steps engine — kustomize-set-image, helm-set-image, git-commit, open-pr, wait-for-merge, health-check
- CLI foundation —
kardinal get pipelines/bundles/steps,kardinal explain,kardinal rollback,kardinal version,kardinal init - Embedded React UI — scaffolded with Vite + React 19, embedded via
go:embed