Skip to content

OTA-1966: Init the Proprosal Lifecycle Controller#1381

Open
hongkailiu wants to merge 7 commits intoopenshift:mainfrom
hongkailiu:OTA-1966
Open

OTA-1966: Init the Proprosal Lifecycle Controller#1381
hongkailiu wants to merge 7 commits intoopenshift:mainfrom
hongkailiu:OTA-1966

Conversation

@hongkailiu
Copy link
Copy Markdown
Member

@hongkailiu hongkailiu commented Apr 22, 2026

It scaffolds the controller that watches (both available and conditional) updates and creates LightSpeed Proposals.

  • The OLS types are dumped into pkg/proposal/api/v1alpha1 so that we can create k8s controller-runtime client for the proposals. We will import them when they are available in LS-operator. There is no static client for it. We may switch to it if available in the future to keep the consistent API style of CVO.

  • It creates ONE testing Proposal with reference to a non-existing workflow and a fake request no matter what updates are in the system. We will fix that in other pulls. The clean up of Proposals will be done later too. The current change is to show the logic of controller and the effective API on the Proposal. The new e2e verifies if the Proposal exists.

Summary by CodeRabbit

  • New Features

    • Added new cluster APIs: Agent, LlmProvider, OLSConfig, Proposal, and Workflow CRDs.
    • Added automated proposal processing that evaluates updates and creates Proposal resources.
  • Refactor

    • Introduced a dedicated proposal controller that can run alongside the updater and be enabled via a new proposal feature gate.
  • Bug Fixes

    • Proposal processing is now enqueued whenever available-update syncs occur.
  • Tests

    • Added an end-to-end test that verifies proposals are created.

@openshift-ci-robot
Copy link
Copy Markdown
Contributor

openshift-ci-robot commented Apr 22, 2026

@hongkailiu: This pull request references OTA-1966 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Apr 22, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a proposal controller and extensive new CRD schemas (Proposal, Workflow, Agent, LlmProvider, OLSConfig); integrates the controller into the Operator lifecycle and enqueues proposal syncs from available-updates; controller reads available-updates and creates Proposal objects in the Kubernetes API.

Changes

Cohort / File(s) Summary
Proposal controller implementation
pkg/proposal/controller.go
New exported Controller with typed rate-limiting queue, NewController, Queue(), QueueKey(), and Sync(ctx,key); Sync calls injected UpdatesGetterFunc, computes proposals, and creates Proposal CRs (ignores AlreadyExists; aggregates other errors).
Operator integration & available-updates
pkg/cvo/cvo.go, pkg/cvo/availableupdates.go
Operator gains proposalController field and initializes it in New(); Run() optionally starts a worker processing proposalController.Queue() (controlled by feature gate) and ensures queue shutdown; syncAvailableUpdates now enqueues the proposal controller queue key after queuing CVO sync.
New Proposal & Workflow APIs
pkg/proposal/api/v1alpha1/proposal_types.go, pkg/proposal/api/v1alpha1/workflow_types.go
Adds Proposal and Workflow CRD types with full spec/status enums, step/sandbox modeling, validation, printcolumns, and scheme registration.
New Agent, LlmProvider, OLSConfig APIs
pkg/proposal/api/v1alpha1/agent_types.go, pkg/proposal/api/v1alpha1/llmprovider_types.go, pkg/proposal/api/v1alpha1/olsconfig_types.go
Introduces cluster-scoped Agent, LlmProvider, and OLSConfig CRDs with detailed specs, enums, XValidation rules, status schemas, printcolumns, and scheme registration.
API scaffolding & deepcopy
pkg/proposal/api/v1alpha1/groupversion_info.go, pkg/proposal/api/v1alpha1/outputfield_deepcopy.go
Adds GroupVersion/SchemeBuilder for agentic.openshift.io/v1alpha1 and manual DeepCopy implementations for output-field related types.
Feature gates
pkg/featuregates/featuregates.go
Adds proposal feature flag to CvoGates and CvoGateChecker via Proposal() bool; sets it based on TechPreviewNoUpgrade.
Build / deps
go.mod
Promoted sigs.k8s.io/controller-runtime v0.22.2 to a direct require and added indirect github.com/evanphx/json-patch/v5.
Tests
test/cvo/accept_risks.go
Adds runtime client setup and a new e2e-style test that mutates ClusterVersion and polls for created Proposal resources in the test namespace.

Sequence Diagram(s)

sequenceDiagram
  participant Operator as Operator
  participant Queue as ProposalQueue
  participant Controller as ProposalController
  participant Updates as AvailableUpdatesProvider
  participant K8s as Kubernetes API

  Operator->>Queue: add(optr.proposalController.QueueKey())
  Note right of Queue: key enqueued
  loop worker
    Queue->>Controller: pop key -> Sync(ctx,key)
    Controller->>Updates: UpdatesGetterFunc() -> releases, conditional updates
    Updates-->>Controller: releases, conditional updates
    Controller->>Controller: compute proposals
    Controller->>K8s: Create Proposal objects
    K8s-->>Controller: Created / AlreadyExists / Error
    Controller-->>Queue: return (error => requeue) or success
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 8 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning The new test case has critical issues: rtClient lacks scheme registration for proposalv1alpha1, config.GetConfigOrDie() should use existing c variable, assertions lack meaningful messages, and test name violates repository conventions without required documentation. Register proposalv1alpha1.AddToScheme(scheme) in rtClient initialization, replace config.GetConfigOrDie() with c variable, add descriptive messages to all Expect calls, and rename test with repository convention including comment explaining Fauxinnati dependency.
Microshift Test Compatibility ⚠️ Warning New test uses ClusterVersion (config.openshift.io/v1 API) without MicroShift protection mechanisms. Add [apigroup:config.openshift.io] tag to test or implement isMicroShift runtime check to skip on MicroShift.
Title check ⚠️ Warning The title contains a typo ('Proprosal' instead of 'Proposal') and describes the main objective of the PR, but the typo undermines clarity. Correct the typo: change 'Proprosal' to 'Proposal' to read 'OTA-1966: Init the Proposal Lifecycle Controller'.
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All test names are static and deterministic with no dynamic components like timestamps, UUIDs, or runtime-dependent data.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The new test performs cluster-wide API operations (ClusterVersion updates and Proposal CRD listing) that function identically on single-node and multi-node clusters without assuming multiple nodes.
Topology-Aware Scheduling Compatibility ✅ Passed The pull request introduces the Proposal Lifecycle Controller and CRDs but does not enforce any topology-dependent scheduling constraints or assumptions about HA infrastructure topology.
Ote Binary Stdout Contract ✅ Passed The pull request does not violate the OTE Binary Stdout Contract. All logging uses klog (which writes to stderr by default, not stdout). The new test case in accept_risks.go runs inside an It() block where Ginkgo intercepts stdout, preventing contract violations. The API package init() functions only register types with SchemeBuilder without emitting output. No process-level main() functions, fmt.Print calls, or uncontrolled stdout writes were found in the modified code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The test properly handles external connectivity requirements via SkipIfNetworkRestricted() and contains no IPv4-specific assumptions or hardcoded addresses.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 22, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: hongkailiu

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 22, 2026
@hongkailiu hongkailiu changed the title OTA-1966: Init the Proprosal Lifecycle Controller [WIP]OTA-1966: Init the Proprosal Lifecycle Controller Apr 22, 2026
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 22, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
pkg/proposal/controller.go (2)

58-63: Avoid dumping the full release list at info level.

%#v on the whole []configv1.Release slice will get noisy quickly once this controller starts reconciling for real. Logging a count, or a compact version list behind V(2), keeps the signal without ballooning normal logs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/controller.go` around lines 58 - 63, Replace the noisy
full-slice info log: instead of klog.Infof("Got available updates: %#v",
updates) log the count at info-level and move the full/compact list to a verbose
level; e.g., use klog.Infof("Got %d available updates", len(updates)) and
klog.V(2).Infof("Available updates: %#v", updates) or build a small slice of
release.Version strings and log that at V(2); update the code around
availableUpdatesGetterFunc, updates and the klog calls accordingly.

18-21: Update the configuration-oriented comments and log messages.

This file is the proposal lifecycle controller, but the queue comment and Sync() logs still say “CVO configuration”. That will send operators to the wrong subsystem when debugging. Please rename them to match proposal lifecycle behavior and replace the placeholder Sync ensures: comment with the actual contract. As per coding guidelines, "When modifying code, check that nearby comments, kubernetes.io/description annotations, and doc strings still accurately describe the new behavior. Outdated documentation is worse than no documentation."

Also applies to: 49-56

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/controller.go` around lines 18 - 21, Update the outdated "CVO
configuration" comments and log messages to refer to the proposal lifecycle
controller and proposal lifecycle behavior: change the field comment for queue
(workqueue.TypedRateLimitingInterface[any]) to describe that it tracks proposal
lifecycle reconciliation tasks, update any Sync() log messages and their logger
context to mention "proposal lifecycle" rather than "CVO configuration", and
replace the placeholder "Sync ensures:" comment with the actual contract of Sync
(state explicitly what invariants Sync enforces, inputs/outputs, and when it
requeues or returns errors). Also search nearby comments and
kubernetes.io/description annotations in this file (including the block around
lines 49–56) and update them consistently to reflect the proposal lifecycle
controller semantics.
pkg/cvo/availableupdates.go (1)

185-186: Gate proposal scheduling with the same enablement check.

Operator.Run() only starts the proposal worker behind shouldEnableProposalController(), but this path always enqueues proposal work. If that switch is ever turned off, the controller is still being scheduled even though nothing drains its queue. Please guard this Add() behind the same helper or move proposal scheduling behind a shared method.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cvo/availableupdates.go` around lines 185 - 186, The proposal queue is
always enqueued even when proposals are disabled; wrap the enqueue call so it
only runs when shouldEnableProposalController() is true (or move both worker
start and enqueue into a single helper). Specifically, guard the call to
optr.proposalController.Queue().Add(optr.proposalController.QueueKey()) with the
same shouldEnableProposalController() check used when starting the proposal
worker (or refactor into a shared method that starts the worker and schedules
the initial Add together).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/cvo/cvo.go`:
- Around line 324-329: The closure passed to optr.proposalController is reading
the shared optr.availableUpdates without holding Operator.statusLock, causing a
data race; modify the closure to call the existing locked accessor (or add one)
that grabs statusLock (RLock), reads/copies the Updates slice, and returns that
copy (do not return the internal slice pointer). Use the Operator accessor
rather than accessing optr.availableUpdates directly and ensure readers use the
same lock discipline as syncAvailableUpdates()/setAvailableUpdates().

---

Nitpick comments:
In `@pkg/cvo/availableupdates.go`:
- Around line 185-186: The proposal queue is always enqueued even when proposals
are disabled; wrap the enqueue call so it only runs when
shouldEnableProposalController() is true (or move both worker start and enqueue
into a single helper). Specifically, guard the call to
optr.proposalController.Queue().Add(optr.proposalController.QueueKey()) with the
same shouldEnableProposalController() check used when starting the proposal
worker (or refactor into a shared method that starts the worker and schedules
the initial Add together).

In `@pkg/proposal/controller.go`:
- Around line 58-63: Replace the noisy full-slice info log: instead of
klog.Infof("Got available updates: %#v", updates) log the count at info-level
and move the full/compact list to a verbose level; e.g., use klog.Infof("Got %d
available updates", len(updates)) and klog.V(2).Infof("Available updates: %#v",
updates) or build a small slice of release.Version strings and log that at V(2);
update the code around availableUpdatesGetterFunc, updates and the klog calls
accordingly.
- Around line 18-21: Update the outdated "CVO configuration" comments and log
messages to refer to the proposal lifecycle controller and proposal lifecycle
behavior: change the field comment for queue
(workqueue.TypedRateLimitingInterface[any]) to describe that it tracks proposal
lifecycle reconciliation tasks, update any Sync() log messages and their logger
context to mention "proposal lifecycle" rather than "CVO configuration", and
replace the placeholder "Sync ensures:" comment with the actual contract of Sync
(state explicitly what invariants Sync enforces, inputs/outputs, and when it
requeues or returns errors). Also search nearby comments and
kubernetes.io/description annotations in this file (including the block around
lines 49–56) and update them consistently to reflect the proposal lifecycle
controller semantics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: be2ee926-1545-4d17-9b6b-0ecd5918efaf

📥 Commits

Reviewing files that changed from the base of the PR and between d3df6c0 and 278bde1.

📒 Files selected for processing (3)
  • pkg/cvo/availableupdates.go
  • pkg/cvo/cvo.go
  • pkg/proposal/controller.go

Comment thread pkg/cvo/cvo.go Outdated
Comment thread pkg/cvo/cvo.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (2)
pkg/cvo/cvo.go (2)

331-335: ⚠️ Potential issue | 🔴 Critical

Don't read optr.availableUpdates directly from the proposal worker.

This closure runs on the proposal worker goroutine without statusLock, and it returns the internal slices directly. syncAvailableUpdates() can replace/sort that state concurrently, so this introduces a real data race and exposes partially updated data.

Suggested fix
 	optr.proposalController = proposal.NewController(func() ([]configv1.Release, []configv1.ConditionalUpdate, error) {
-		if optr.availableUpdates == nil {
+		availableUpdates := optr.getAvailableUpdates()
+		if availableUpdates == nil {
 			return nil, nil, nil
 		}
-		return optr.availableUpdates.Updates, optr.availableUpdates.ConditionalUpdates, nil
+		return append([]configv1.Release(nil), availableUpdates.Updates...),
+			append([]configv1.ConditionalUpdate(nil), availableUpdates.ConditionalUpdates...),
+			nil
 	}, c)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cvo/cvo.go` around lines 331 - 335, The closure passed to
proposal.NewController reads optr.availableUpdates without holding the
optr.statusLock, causing a data race; modify the closure in
optr.proposalController so it acquires optr.statusLock (or otherwise
synchronizes with syncAvailableUpdates()), checks for nil, and returns deep
copies of the slices (make new slices and copy elements from
optr.availableUpdates.Updates and .ConditionalUpdates) instead of returning the
internal slices directly; this ensures the proposal worker never accesses
partially-updated shared state.

547-558: ⚠️ Potential issue | 🟠 Major

Don't hard-enable the proposal worker yet.

shouldEnableProposalController() always returns true, so every CVO instance starts this controller even when the Proposal API/CRDs or feature-gate prerequisites are absent. In those clusters this worker will just spin on failing creates and add noisy retries.

Also applies to: 1228-1231

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cvo/cvo.go` around lines 547 - 558, The current startup unconditionally
launches the proposal controller because shouldEnableProposalController() always
returns true; change the guard so the controller only starts after verifying the
Proposal API/CRDs and feature gate are present. Replace the current conditional
that checks shouldEnableProposalController() (and the identical checks at the
other location) with a call that verifies API availability and the feature gate
(for example implement optr.canStartProposalController() which uses the API
discovery/RESTMapper or ServerResourcesForGroupVersion to confirm the Proposal
CRD/group-version exists and checks the feature gate via
utilfeature.DefaultFeatureGate.Enabled), and only then start the goroutine that
calls optr.worker(..., optr.proposalController.Queue(),
optr.proposalController.Sync); otherwise log that the proposal controller is
disabled. Ensure the new check is used in both places mentioned.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/cvo/cvo.go`:
- Around line 327-330: The constructor currently calls config.GetConfigOrDie()
which exits the process on failure and violates this function's error-return
contract; change the code to obtain a kubeconfig that returns (cfg, err) (e.g.
use a non-fatal API such as config.GetConfig() or
clientcmd.BuildConfigFromFlags()/rest.InClusterConfig wrapper), check and return
the error if present, then pass the cfg into runtimeclient.New(cfg,
runtimeclient.Options{}); ensure you remove the GetConfigOrDie() call so New()'s
error can be propagated to the caller.

In `@pkg/proposal/controller.go`:
- Around line 92-106: getProposals currently ignores its inputs and always
returns one hard-coded Proposal; change it to iterate over the updates and
conditionalUpdates parameters and construct a Proposal for each real update (or
a single Proposal that encodes the update set) instead of the fixed-name item.
Use the updates and conditionalUpdates slices to populate Proposal.Spec.Request,
WorkflowRef and metadata (avoid using controllerName as the constant Name);
reference the getProposals function, the updates and conditionalUpdates
parameters, and proposalv1alpha1.Proposal/ProposalSpec to build appropriately
named/unique Proposals (or a single Proposal whose Spec contains the serialized
update list) so the emitted resources reflect the actual update data passed in.
- Around line 78-88: The loop in the controller that calls c.client.Create for
each item in proposals must reconcile existing resources instead of ignoring
kapierrors.IsAlreadyExists: for each proposal returned by the reconciler
(variable proposals and loop), attempt to Get the existing object (using the
same name/namespace/UID), if not found fall back to Create, but if found compare
the desired spec/labels/annotations and call c.client.Update or Patch to apply
changes; additionally detect proposals present in cluster but absent from the
desired proposals list and delete them via c.client.Delete; aggregate and return
any Create/Get/Update/Delete errors (replace the current IsAlreadyExists drop
logic around c.client.Create with this reconcile flow referencing
c.client.Create, c.client.Get, c.client.Update, and c.client.Delete).
- Around line 25-27: Update all copied comments, log messages, and
kubernetes.io/description annotations that refer to "CVO configuration" to
correctly refer to the proposal controller behavior (e.g., "proposal
controller", "proposal configuration", or similar) so they match the actual
code; specifically edit the comment above the queue variable that currently
mentions "CVO configuration" and any other comments/log lines around the queue
and the cvo.Operator worker method (the type any usage) as well as the doc
strings referenced near the same block (previously lines noted around 59-65) to
use accurate wording. Ensure phrasing is consistent across comments, logs, and
annotations and preserves the note about using type any to satisfy
cvo.Operator's worker signature.

---

Duplicate comments:
In `@pkg/cvo/cvo.go`:
- Around line 331-335: The closure passed to proposal.NewController reads
optr.availableUpdates without holding the optr.statusLock, causing a data race;
modify the closure in optr.proposalController so it acquires optr.statusLock (or
otherwise synchronizes with syncAvailableUpdates()), checks for nil, and returns
deep copies of the slices (make new slices and copy elements from
optr.availableUpdates.Updates and .ConditionalUpdates) instead of returning the
internal slices directly; this ensures the proposal worker never accesses
partially-updated shared state.
- Around line 547-558: The current startup unconditionally launches the proposal
controller because shouldEnableProposalController() always returns true; change
the guard so the controller only starts after verifying the Proposal API/CRDs
and feature gate are present. Replace the current conditional that checks
shouldEnableProposalController() (and the identical checks at the other
location) with a call that verifies API availability and the feature gate (for
example implement optr.canStartProposalController() which uses the API
discovery/RESTMapper or ServerResourcesForGroupVersion to confirm the Proposal
CRD/group-version exists and checks the feature gate via
utilfeature.DefaultFeatureGate.Enabled), and only then start the goroutine that
calls optr.worker(..., optr.proposalController.Queue(),
optr.proposalController.Sync); otherwise log that the proposal controller is
disabled. Ensure the new check is used in both places mentioned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 2cbd6bae-c859-4116-ac7b-579b5dbc890a

📥 Commits

Reviewing files that changed from the base of the PR and between 278bde1 and d33bcbb.

📒 Files selected for processing (3)
  • pkg/cvo/availableupdates.go
  • pkg/cvo/cvo.go
  • pkg/proposal/controller.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/cvo/availableupdates.go

Comment thread pkg/cvo/cvo.go Outdated
Comment thread pkg/proposal/controller.go Outdated
Comment thread pkg/proposal/controller.go
Comment thread pkg/proposal/controller.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
pkg/proposal/api/v1alpha1/olsconfig_types.go (2)

157-158: Document the shared type reference.

The comment in agent_types.go (lines 157-158) references that MCPServerConfig and related types are defined here. Consider adding a reciprocal comment here noting these types are shared with the Agent API, to help maintainers understand the coupling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/olsconfig_types.go` around lines 157 - 158, Add a
reciprocal comment above the shared types in olsconfig_types.go indicating that
MCPServerConfig (and any related types) are shared with the Agent API
(referenced from agent_types.go) so maintainers understand this coupling; place
the note near the LogLevel comment block and explicitly name MCPServerConfig and
the related types to make the cross-file dependency discoverable.

1-2: Inconsistent copyright year.

This file uses "Copyright 2024" while other new files in this PR use "Copyright 2026". Consider updating for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/olsconfig_types.go` around lines 1 - 2, Update the
top-of-file copyright year in pkg/proposal/api/v1alpha1/olsconfig_types.go to
match the rest of the PR (change "Copyright 2024" to "Copyright 2026") by
editing the file header comment at the very top of the file.
pkg/proposal/api/v1alpha1/proposal_types.go (1)

175-193: Consider making Actions field explicitly required or optional.

ProposalResult.Actions (line 180) lacks both +kubebuilder:validation:Required and +optional markers. If an empty actions list is invalid, add a MinItems=1 validation. If it can be empty, add +optional for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/proposal_types.go` around lines 175 - 193,
ProposalResult.Actions is missing explicit kubebuilder validation; decide
whether the Actions slice must be non-empty or may be omitted and update
ProposalResult accordingly: if an empty actions list is invalid add a
kubebuilder validation tag like `+kubebuilder:validation:MinItems=1` on the
Actions field (ProposedAction/Actions) so the API enforces at least one item,
otherwise mark the field optional by adding `+optional` to clarify it may be
omitted or empty.
pkg/proposal/api/v1alpha1/llmprovider_types.go (1)

58-63: Clarify namespace resolution for LocalObjectReference on cluster-scoped resource.

corev1.LocalObjectReference typically references objects in the same namespace. Since LlmProvider is cluster-scoped, the documentation should clarify which namespace the operator looks for the secret in (e.g., operator's namespace, or a well-known namespace). Consider updating the comment at line 58-61 to specify the expected namespace.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/llmprovider_types.go` around lines 58 - 63, The
comment for the CredentialsSecretRef field is ambiguous about namespace
resolution for corev1.LocalObjectReference on the cluster-scoped LlmProvider;
update the field comment (around CredentialsSecretRef in llmprovider_types.go)
to explicitly state which namespace the operator will look up the Secret in (for
example: "operator's namespace" or a specific well-known namespace), and mention
that the reference is expected to point to a Secret in that namespace so callers
know where to place credentials and how they will be injected at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/proposal/api/v1alpha1/agent_types.go`:
- Around line 240-256: Add a kubebuilder XValidation marker to enforce the
documented mutual exclusion between OutputFields and RawOutputSchema: add a
validation rule on the Agent or AgentSpec type that forbids having both
spec.outputFields (non-empty) and spec.rawOutputSchema set at the same time,
using an XValidation rule like the suggested negated has/size expression and a
message "outputFields and rawOutputSchema are mutually exclusive"; target the
type where OutputFields []OutputField and RawOutputSchema *apiextensionsv1.JSON
are declared so the CRD prevents admission when both are provided.

---

Nitpick comments:
In `@pkg/proposal/api/v1alpha1/llmprovider_types.go`:
- Around line 58-63: The comment for the CredentialsSecretRef field is ambiguous
about namespace resolution for corev1.LocalObjectReference on the cluster-scoped
LlmProvider; update the field comment (around CredentialsSecretRef in
llmprovider_types.go) to explicitly state which namespace the operator will look
up the Secret in (for example: "operator's namespace" or a specific well-known
namespace), and mention that the reference is expected to point to a Secret in
that namespace so callers know where to place credentials and how they will be
injected at runtime.

In `@pkg/proposal/api/v1alpha1/olsconfig_types.go`:
- Around line 157-158: Add a reciprocal comment above the shared types in
olsconfig_types.go indicating that MCPServerConfig (and any related types) are
shared with the Agent API (referenced from agent_types.go) so maintainers
understand this coupling; place the note near the LogLevel comment block and
explicitly name MCPServerConfig and the related types to make the cross-file
dependency discoverable.
- Around line 1-2: Update the top-of-file copyright year in
pkg/proposal/api/v1alpha1/olsconfig_types.go to match the rest of the PR (change
"Copyright 2024" to "Copyright 2026") by editing the file header comment at the
very top of the file.

In `@pkg/proposal/api/v1alpha1/proposal_types.go`:
- Around line 175-193: ProposalResult.Actions is missing explicit kubebuilder
validation; decide whether the Actions slice must be non-empty or may be omitted
and update ProposalResult accordingly: if an empty actions list is invalid add a
kubebuilder validation tag like `+kubebuilder:validation:MinItems=1` on the
Actions field (ProposedAction/Actions) so the API enforces at least one item,
otherwise mark the field optional by adding `+optional` to clarify it may be
omitted or empty.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: c2f541c2-528f-4d4b-9c6f-165464e20294

📥 Commits

Reviewing files that changed from the base of the PR and between d33bcbb and dfc0fb2.

⛔ Files ignored due to path filters (47)
  • go.sum is excluded by !**/*.sum, !go.sum
  • pkg/proposal/api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • vendor/github.com/evanphx/json-patch/v5/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/errors.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/decode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/encode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/fold.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/fuzz.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/indent.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/scanner.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/stream.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/tables.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/tags.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/merge.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/patch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme/register.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/client-go/metadata/interface.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/client-go/metadata/metadata.go is excluded by !vendor/**, !**/vendor/**
  • vendor/modules.txt is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/errors.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/restmapper.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/applyconfigurations.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/client_rest_resources.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/log/log.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/warning_handler.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (8)
  • go.mod
  • pkg/proposal/api/v1alpha1/agent_types.go
  • pkg/proposal/api/v1alpha1/groupversion_info.go
  • pkg/proposal/api/v1alpha1/llmprovider_types.go
  • pkg/proposal/api/v1alpha1/olsconfig_types.go
  • pkg/proposal/api/v1alpha1/outputfield_deepcopy.go
  • pkg/proposal/api/v1alpha1/proposal_types.go
  • pkg/proposal/api/v1alpha1/workflow_types.go

Comment on lines +240 to +256
// outputFields defines additional structured output fields beyond the
// base schema that every agent produces (diagnosis, proposal, RBAC,
// verification plan). Use this to request domain-specific structured
// data from the agent (e.g., an ACS violation ID, affected images).
// Mutually exclusive with rawOutputSchema.
// +optional
// +listType=map
// +listMapKey=name
OutputFields []OutputField `json:"outputFields,omitempty"`

// rawOutputSchema is an escape hatch that replaces the entire output
// schema with a raw JSON Schema object. Use this when outputFields
// cannot express the schema you need (e.g., deeply nested structures,
// conditional fields). Mutually exclusive with outputFields.
// +optional
RawOutputSchema *apiextensionsv1.JSON `json:"rawOutputSchema,omitempty"`
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing XValidation for mutual exclusion between outputFields and rawOutputSchema.

The documentation states these fields are mutually exclusive (lines 244, 253), but there's no XValidation rule to enforce this at admission time. Users could set both, leading to undefined behavior.

🛠️ Suggested fix - Add XValidation to AgentSpec or Agent

Add this validation marker to the Agent type or AgentSpec:

// +kubebuilder:validation:XValidation:rule="!(has(self.spec.outputFields) && size(self.spec.outputFields) > 0 && has(self.spec.rawOutputSchema))",message="outputFields and rawOutputSchema are mutually exclusive"

Or on AgentSpec:

// +kubebuilder:validation:XValidation:rule="!(has(self.outputFields) && size(self.outputFields) > 0 && has(self.rawOutputSchema))",message="outputFields and rawOutputSchema are mutually exclusive"
type AgentSpec struct {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/agent_types.go` around lines 240 - 256, Add a
kubebuilder XValidation marker to enforce the documented mutual exclusion
between OutputFields and RawOutputSchema: add a validation rule on the Agent or
AgentSpec type that forbids having both spec.outputFields (non-empty) and
spec.rawOutputSchema set at the same time, using an XValidation rule like the
suggested negated has/size expression and a message "outputFields and
rawOutputSchema are mutually exclusive"; target the type where OutputFields
[]OutputField and RawOutputSchema *apiextensionsv1.JSON are declared so the CRD
prevents admission when both are provided.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
pkg/proposal/controller.go (4)

59-66: ⚠️ Potential issue | 🟡 Minor

Log messages reference "CVO configuration" instead of proposal controller.

The log messages at lines 63 and 65 say "syncing CVO configuration" but this is the proposal controller. Update these to reflect the actual purpose (e.g., "proposal sync" or similar).

As per coding guidelines, comments and logs should accurately describe the behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/controller.go` around lines 59 - 66, The log messages in
Controller.Sync incorrectly mention "CVO configuration"; update the two
klog.V(i.Normal).Infof calls inside the Sync method (the start log and the
deferred finish log that reference startTime) to accurately describe this as the
proposal controller (e.g., "Started proposal sync %q" and "Finished proposal
sync (%v)"). Locate the Sync function on type Controller and replace the message
strings passed to Infof while keeping the same formatting arguments.

92-108: ⚠️ Potential issue | 🟠 Major

Placeholder implementation does not use update data.

The getProposals function ignores its updates and conditionalUpdates parameters and returns a single hardcoded Proposal. The TODO comment acknowledges this, but the controller cannot represent actual available updates until this is implemented.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/controller.go` around lines 92 - 108, getProposals currently
ignores its parameters (updates, conditionalUpdates) and returns a single
hardcoded proposal; update it to build and return Proposal objects based on the
provided updates and conditionalUpdates so the controller represents real
available updates. Specifically, iterate over the updates and/or
conditionalUpdates in getProposals, populate ProposalSpec fields (e.g., Request,
WorkflowRef, and any metadata) using values from each
configv1.Release/configv1.ConditionalUpdate, create one proposal per relevant
update (or aggregate into meaningful proposals as required), and return the
slice of *proposalv1alpha1.Proposal instead of the static literal; ensure you
preserve required fields (ObjectMeta.Name/Namespace) and set WorkflowRef
appropriately rather than using the hardcoded "ota-advisory".

78-88: ⚠️ Potential issue | 🟠 Major

Reconcile existing proposals instead of dropping AlreadyExists.

The current implementation only creates proposals and ignores AlreadyExists errors. This is write-once behavior — changes to the desired set of proposals are never reconciled. A lifecycle controller typically needs Get/Update/Delete logic to converge state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/controller.go` around lines 78 - 88, The loop in
pkg/proposal/controller.go currently calls c.client.Create for each proposal and
ignores AlreadyExists errors (using kapierrors.IsAlreadyExists), which prevents
reconciling updates; change the logic in the proposals iteration to attempt a
Get (c.client.Get) when Create returns AlreadyExists, compare the existing
object with the desired proposal, and call Update (c.client.Update) when they
differ (or Delete when the proposal should be removed); ensure you still
aggregate non-AlreadyExists errors into errs and return
kutilerrors.NewAggregate(errs), and reference the existing symbols
c.client.Create, kapierrors.IsAlreadyExists, c.client.Get, and c.client.Update
to implement the reconcile path.

25-27: ⚠️ Potential issue | 🟡 Minor

Rename the copied CVO-configuration docs and logs.

The comment still references "CVO configuration" but this is the proposal controller. This creates confusion during debugging and maintenance.

As per coding guidelines, "When modifying code, check that nearby comments, kubernetes.io/description annotations, and doc strings still accurately describe the new behavior."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/controller.go` around lines 25 - 27, Update the misleading "CVO
configuration" references in pkg/proposal/controller.go: change the comment
above the queue variable and any nearby log messages, kubernetes.io/description
annotations, and docstrings that mention "CVO configuration" to accurately say
"proposal controller" (or the specific proposal responsibility) so they reflect
the actual behavior of the Proposal controller; search for the symbol queue and
any functions/methods in this file (e.g., the controller struct and its worker
methods) and update all surrounding comments and logs consistently.
pkg/proposal/api/v1alpha1/agent_types.go (1)

240-256: ⚠️ Potential issue | 🟡 Minor

Missing XValidation for mutual exclusion between outputFields and rawOutputSchema.

The documentation states these fields are mutually exclusive (lines 244, 253), but there's no XValidation rule to enforce this at admission time. Users could set both, leading to undefined behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/agent_types.go` around lines 240 - 256, Add a
kubebuilder XValidation rule to enforce that OutputFields and RawOutputSchema
are mutually exclusive by adding a marker like
+kubebuilder:validation:XValidation:rule="self.rawOutputSchema == null ||
size(self.outputFields) == 0",message="only one of outputFields or
rawOutputSchema may be set" to the Agent spec struct (near the definitions of
OutputFields and RawOutputSchema) so admission rejects objects that set both
OutputFields and RawOutputSchema.
🧹 Nitpick comments (2)
pkg/proposal/api/v1alpha1/proposal_types.go (1)

224-234: Consider adding +listType=map with +listMapKey=name to VerificationPlan.Steps.

VerificationStep has a Name field that appears to be a unique identifier. Adding list type annotations would enable strategic merge patch semantics and ensure stable ordering in the OpenAPI schema, consistent with how Properties is annotated in OutputField (line 94-96 of agent_types.go).

♻️ Suggested annotation
 type VerificationPlan struct {
 	// description is a human-readable summary of the verification approach.
 	Description string `json:"description"`
 	// steps is the ordered list of verification checks to run.
 	// +optional
+	// +listType=map
+	// +listMapKey=name
 	Steps []VerificationStep `json:"steps,omitempty"`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/proposal_types.go` around lines 224 - 234, The
Steps slice on VerificationPlan should be annotated as a map keyed by each
step's Name to enable stable ordering and strategic merge patch behavior; update
the VerificationPlan definition to add the Kubernetes markers "+listType=map"
and "+listMapKey=name" above the Steps field (reference VerificationPlan.Steps
and the unique identifier field VerificationStep.Name) similar to how
OutputField.Properties is annotated; ensure the tag placement follows other CRD
markers so generated OpenAPI schema and strategic merge patches treat Steps as a
keyed map.
pkg/proposal/controller.go (1)

97-97: Consider using empty string directly instead of metav1.NamespaceAll.

metav1.NamespaceAll is semantically meant for list operations ("list across all namespaces"). For a cluster-scoped resource where Namespace should be empty, consider using "" directly or simply omitting the field to avoid confusion.

♻️ Suggested clarification
 		{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      controllerName,
-				Namespace: metav1.NamespaceAll,
+				// Namespace is empty for cluster-scoped resources
 			},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/controller.go` at line 97, Replace the use of
metav1.NamespaceAll in the object literal (the line "Namespace:
metav1.NamespaceAll") with an empty string or omit the Namespace field entirely
for this cluster-scoped resource; update the struct/variable where "Namespace:
metav1.NamespaceAll" appears so it uses "" (or drops the Namespace key) to avoid
the misleading list-scoped semantic of metav1.NamespaceAll.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/proposal/api/v1alpha1/agent_types.go`:
- Around line 99-130: OutputSubField currently allows Type values from
OutputFieldType (including "object") but doesn't provide a Properties field to
define object schemas; update the type definition to prevent invalid state by
either adding a Properties map field (e.g., Properties map[string]OutputSubField
`json:"properties,omitempty"`) to OutputSubField and document/use it when Type
== "object", or create a new enum/type OutputSubFieldType (or a constrained
validation) that excludes "object" and replace OutputSubField.Type with that new
type; refer to OutputSubField, OutputFieldType, and Properties when making the
change so consumers can't declare type: "object" without a schema.

In `@pkg/proposal/api/v1alpha1/olsconfig_types.go`:
- Line 2: Update the copyright header year in the top-of-file comment from 2024
to 2026 to match the other generated types (e.g., the headers used in
llmprovider_types.go, agent_types.go, proposal_types.go); locate the file's
header comment (the package-level/top comment in olsconfig_types.go) and change
the year value to 2026 for consistency.

---

Duplicate comments:
In `@pkg/proposal/api/v1alpha1/agent_types.go`:
- Around line 240-256: Add a kubebuilder XValidation rule to enforce that
OutputFields and RawOutputSchema are mutually exclusive by adding a marker like
+kubebuilder:validation:XValidation:rule="self.rawOutputSchema == null ||
size(self.outputFields) == 0",message="only one of outputFields or
rawOutputSchema may be set" to the Agent spec struct (near the definitions of
OutputFields and RawOutputSchema) so admission rejects objects that set both
OutputFields and RawOutputSchema.

In `@pkg/proposal/controller.go`:
- Around line 59-66: The log messages in Controller.Sync incorrectly mention
"CVO configuration"; update the two klog.V(i.Normal).Infof calls inside the Sync
method (the start log and the deferred finish log that reference startTime) to
accurately describe this as the proposal controller (e.g., "Started proposal
sync %q" and "Finished proposal sync (%v)"). Locate the Sync function on type
Controller and replace the message strings passed to Infof while keeping the
same formatting arguments.
- Around line 92-108: getProposals currently ignores its parameters (updates,
conditionalUpdates) and returns a single hardcoded proposal; update it to build
and return Proposal objects based on the provided updates and conditionalUpdates
so the controller represents real available updates. Specifically, iterate over
the updates and/or conditionalUpdates in getProposals, populate ProposalSpec
fields (e.g., Request, WorkflowRef, and any metadata) using values from each
configv1.Release/configv1.ConditionalUpdate, create one proposal per relevant
update (or aggregate into meaningful proposals as required), and return the
slice of *proposalv1alpha1.Proposal instead of the static literal; ensure you
preserve required fields (ObjectMeta.Name/Namespace) and set WorkflowRef
appropriately rather than using the hardcoded "ota-advisory".
- Around line 78-88: The loop in pkg/proposal/controller.go currently calls
c.client.Create for each proposal and ignores AlreadyExists errors (using
kapierrors.IsAlreadyExists), which prevents reconciling updates; change the
logic in the proposals iteration to attempt a Get (c.client.Get) when Create
returns AlreadyExists, compare the existing object with the desired proposal,
and call Update (c.client.Update) when they differ (or Delete when the proposal
should be removed); ensure you still aggregate non-AlreadyExists errors into
errs and return kutilerrors.NewAggregate(errs), and reference the existing
symbols c.client.Create, kapierrors.IsAlreadyExists, c.client.Get, and
c.client.Update to implement the reconcile path.
- Around line 25-27: Update the misleading "CVO configuration" references in
pkg/proposal/controller.go: change the comment above the queue variable and any
nearby log messages, kubernetes.io/description annotations, and docstrings that
mention "CVO configuration" to accurately say "proposal controller" (or the
specific proposal responsibility) so they reflect the actual behavior of the
Proposal controller; search for the symbol queue and any functions/methods in
this file (e.g., the controller struct and its worker methods) and update all
surrounding comments and logs consistently.

---

Nitpick comments:
In `@pkg/proposal/api/v1alpha1/proposal_types.go`:
- Around line 224-234: The Steps slice on VerificationPlan should be annotated
as a map keyed by each step's Name to enable stable ordering and strategic merge
patch behavior; update the VerificationPlan definition to add the Kubernetes
markers "+listType=map" and "+listMapKey=name" above the Steps field (reference
VerificationPlan.Steps and the unique identifier field VerificationStep.Name)
similar to how OutputField.Properties is annotated; ensure the tag placement
follows other CRD markers so generated OpenAPI schema and strategic merge
patches treat Steps as a keyed map.

In `@pkg/proposal/controller.go`:
- Line 97: Replace the use of metav1.NamespaceAll in the object literal (the
line "Namespace: metav1.NamespaceAll") with an empty string or omit the
Namespace field entirely for this cluster-scoped resource; update the
struct/variable where "Namespace: metav1.NamespaceAll" appears so it uses "" (or
drops the Namespace key) to avoid the misleading list-scoped semantic of
metav1.NamespaceAll.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 14ae3070-e8d6-43c4-81b5-3f8be460afd7

📥 Commits

Reviewing files that changed from the base of the PR and between dfc0fb2 and cc82fde.

⛔ Files ignored due to path filters (47)
  • go.sum is excluded by !**/*.sum, !go.sum
  • pkg/proposal/api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • vendor/github.com/evanphx/json-patch/v5/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/errors.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/decode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/encode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/fold.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/fuzz.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/indent.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/scanner.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/stream.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/tables.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/internal/json/tags.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/merge.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/evanphx/json-patch/v5/patch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme/register.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/client-go/metadata/interface.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/client-go/metadata/metadata.go is excluded by !vendor/**, !**/vendor/**
  • vendor/modules.txt is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/errors.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/restmapper.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/applyconfigurations.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/client_rest_resources.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/codec.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/config/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/metadata_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/object.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/client/watch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/internal/log/log.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go is excluded by !vendor/**, !**/vendor/**
  • vendor/sigs.k8s.io/controller-runtime/pkg/log/warning_handler.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (12)
  • go.mod
  • pkg/cvo/availableupdates.go
  • pkg/cvo/cvo.go
  • pkg/featuregates/featuregates.go
  • pkg/proposal/api/v1alpha1/agent_types.go
  • pkg/proposal/api/v1alpha1/groupversion_info.go
  • pkg/proposal/api/v1alpha1/llmprovider_types.go
  • pkg/proposal/api/v1alpha1/olsconfig_types.go
  • pkg/proposal/api/v1alpha1/outputfield_deepcopy.go
  • pkg/proposal/api/v1alpha1/proposal_types.go
  • pkg/proposal/api/v1alpha1/workflow_types.go
  • pkg/proposal/controller.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/proposal/api/v1alpha1/groupversion_info.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/cvo/availableupdates.go
  • go.mod
  • pkg/cvo/cvo.go
  • pkg/proposal/api/v1alpha1/workflow_types.go

Comment on lines +99 to +130
// OutputSubField defines a nested field (one level deep) within an OutputField
// of type "object" or within array items of type "object". At this depth,
// array items are restricted to primitive types (string, number, boolean).
// +kubebuilder:validation:XValidation:rule="self.type == 'array' ? has(self.items) : true",message="items is required when type is array"
// +kubebuilder:validation:XValidation:rule="has(self.enum) ? self.type == 'string' : true",message="enum is only valid for string fields"
type OutputSubField struct {
// name is the field name in the output JSON.
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern="^[a-zA-Z][a-zA-Z0-9_]*$"
Name string `json:"name"`

// type is the JSON type of this field.
// +kubebuilder:validation:Required
Type OutputFieldType `json:"type"`

// description explains the purpose of this field (passed to the LLM).
// +optional
Description string `json:"description,omitempty"`

// required indicates whether the agent must populate this field.
// +optional
Required bool `json:"required,omitempty"`

// enum constrains string fields to a set of allowed values.
// +optional
Enum []string `json:"enum,omitempty"`

// items defines the element schema when type is array (primitive types only at this depth).
// +optional
Items *OutputSubFieldItems `json:"items,omitempty"`
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

OutputSubField allows type: object but has no properties field.

OutputSubField.Type uses OutputFieldType which includes object, but unlike OutputField, OutputSubField does not have a Properties field. This means users can set type: object on a sub-field but cannot define its schema.

Either:

  1. Add a Properties field to OutputSubField, or
  2. Restrict OutputSubField.Type to exclude object via a dedicated enum
🛠️ Option 2: Restrict the enum
 type OutputSubField struct {
 	// ...
 	// type is the JSON type of this field.
 	// +kubebuilder:validation:Required
+	// +kubebuilder:validation:Enum=string;number;boolean;array
 	Type OutputFieldType `json:"type"`

Or define a separate OutputSubFieldType enum that excludes object.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/agent_types.go` around lines 99 - 130,
OutputSubField currently allows Type values from OutputFieldType (including
"object") but doesn't provide a Properties field to define object schemas;
update the type definition to prevent invalid state by either adding a
Properties map field (e.g., Properties map[string]OutputSubField
`json:"properties,omitempty"`) to OutputSubField and document/use it when Type
== "object", or create a new enum/type OutputSubFieldType (or a constrained
validation) that excludes "object" and replace OutputSubField.Type with that new
type; refer to OutputSubField, OutputFieldType, and Properties when making the
change so consumers can't declare type: "object" without a schema.

@@ -0,0 +1,871 @@
/*
Copyright 2024.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor: Copyright year inconsistency.

This file uses Copyright 2024 while other new files in this PR (e.g., llmprovider_types.go, agent_types.go, proposal_types.go) use Copyright 2026. Consider aligning for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/proposal/api/v1alpha1/olsconfig_types.go` at line 2, Update the copyright
header year in the top-of-file comment from 2024 to 2026 to match the other
generated types (e.g., the headers used in llmprovider_types.go, agent_types.go,
proposal_types.go); locate the file's header comment (the package-level/top
comment in olsconfig_types.go) and change the year value to 2026 for
consistency.

@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 22, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test/cvo/accept_risks.go (1)

264-273: Harden proposal polling to avoid false positives and fix stale failure text.

The current len(proposals.Items) > 0 check can pass on unrelated pre-existing proposals, and the failure message still references conditional update risks.

Proposed refinement
-		g.By("Checking if the proposal are created")
+		g.By("Checking if proposals are created")
 		o.Expect(wait.PollUntilContextTimeout(ctx, 30*time.Second, 5*time.Minute, true, func(ctx context.Context) (done bool, err error) {
 			proposals := proposalv1alpha1.ProposalList{}
 			err = rtClient.List(ctx, &proposals, runtimeclient.InNamespace(external.DefaultCVONamespace))
 			o.Expect(err).NotTo(o.HaveOccurred())
-			if len(proposals.Items) == 0 {
-				return false, nil
+			for _, p := range proposals.Items {
+				if p.Name == "test-proposal" {
+					return true, nil
+				}
 			}
-			return true, nil
-		})).NotTo(o.HaveOccurred(), "no conditional update risk from alert found in ClusterVersion's status")
+			return false, nil
+		})).NotTo(o.HaveOccurred(), "no proposals found in ClusterVersion namespace")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/cvo/accept_risks.go` around lines 264 - 273, The polling currently
treats any existing Proposal as success, causing false positives and leaving an
incorrect failure message; modify the wait loop that lists
proposalv1alpha1.ProposalList via rtClient.List (namespace
external.DefaultCVONamespace) inside wait.PollUntilContextTimeout to filter
proposals for the specific alert/caller (e.g., by checking a known label,
annotation or ownerReference added when creating the proposal) and only return
true when a matching proposal is found, and update the final error text passed
to NotTo(o.HaveOccurred()) to reference the correct missing proposal (not
"conditional update risk from alert").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/cvo/accept_risks.go`:
- Around line 249-250: Rename the g.It test description to the Jira-title format
(e.g. start with [Jira:"Cluster Version Operator"] Create proposals) and add a
short comment immediately above the util.SkipIfNetworkRestricted call explaining
why access to util.FauxinnatiAPIURL is required for this e2e (external endpoint
used to simulate/validate proposal creation), then keep the existing skip by
calling util.SkipIfNetworkRestricted(ctx, c, util.FauxinnatiAPIURL); ensure you
update the g.It text only (the test body and skip call remain) so the test name
and the rationale comment follow repo conventions.
- Around line 53-54: The runtime client is created with
runtimeclient.New(config.GetConfigOrDie(), ...) without registering the
proposalv1alpha1 scheme and ignores the available REST config variable c; update
the setup to register proposalv1alpha1 into the client scheme before calling
runtimeclient.New and pass the existing c (from util.GetRestConfig()) instead of
calling config.GetConfigOrDie(); ensure the scheme registration covers the types
used by ProposalList and other proposal operations. Also add brief E2E-style
comments where the skip is performed to explain the network restriction and the
external endpoint, and update the assertion message used when checking proposals
(the check currently mentions "conditional update risk from alert") to reference
proposals instead of alerts/risks.

---

Nitpick comments:
In `@test/cvo/accept_risks.go`:
- Around line 264-273: The polling currently treats any existing Proposal as
success, causing false positives and leaving an incorrect failure message;
modify the wait loop that lists proposalv1alpha1.ProposalList via rtClient.List
(namespace external.DefaultCVONamespace) inside wait.PollUntilContextTimeout to
filter proposals for the specific alert/caller (e.g., by checking a known label,
annotation or ownerReference added when creating the proposal) and only return
true when a matching proposal is found, and update the final error text passed
to NotTo(o.HaveOccurred()) to reference the correct missing proposal (not
"conditional update risk from alert").
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 19fc22b0-5335-4f36-8ac5-7facc9dca7e4

📥 Commits

Reviewing files that changed from the base of the PR and between cc82fde and 1acd4c0.

📒 Files selected for processing (2)
  • pkg/proposal/controller.go
  • test/cvo/accept_risks.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/proposal/controller.go

Comment thread test/cvo/accept_risks.go Outdated
Comment thread test/cvo/accept_risks.go Outdated
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 22, 2026
@hongkailiu hongkailiu force-pushed the OTA-1966 branch 8 times, most recently from deaae2c to f583aba Compare April 23, 2026 03:12
@hongkailiu
Copy link
Copy Markdown
Member Author

The new case is in the 2nd shard.
https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1381/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-ovn-techpreview-serial-2of3/2047151689802190848

$ curl -s https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1381/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-ovn-techpreview-serial-2of3/2047151689802190848/artifacts/e2e-agnostic-ovn-techpreview-serial/openshift-e2e-test/artifacts/e2e.log | grep proposals
started: 0/1/28 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"
passed: (2s) 2026-04-23T05:06:37 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

@hongkailiu hongkailiu force-pushed the OTA-1966 branch 3 times, most recently from 5760c92 to 01fdc4c Compare April 23, 2026 15:00
@hongkailiu
Copy link
Copy Markdown
Member Author

/retest-required

1 similar comment
@hongkailiu
Copy link
Copy Markdown
Member Author

/retest-required

@hongkailiu
Copy link
Copy Markdown
Member Author

Taken from this job: Seems retried automatically. This is new to me.

# https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1381/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-ovn-techpreview-serial-2of3/2047329783963979776/artifacts/e2e-agnostic-ovn-techpreview-serial/openshift-e2e-test/artifacts/e2e.log

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (2.8s) 2026-04-23T17:56:33 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

  STEP: Using fauxinnati as the upstream and its simple channel @ 04/23/26 17:56:36.305
  STEP: Checking if the proposal are created @ 04/23/26 17:56:36.389
  [FAILED] in [AfterEach] - /go/src/github.com/openshift/cluster-version-operator/test/cvo/proposal.go:80 @ 04/23/26 17:56:36.784

fail [github.com/openshift/cluster-version-operator/test/cvo/proposal.go:80]: Unexpected error:
    <*errors.StatusError | 0xc000282960>: 
    Operation cannot be fulfilled on clusterversions.config.openshift.io "version": the object has been modified; please apply your changes to the latest version and try again
    {
        ErrStatus: {
            TypeMeta: {Kind: "", APIVersion: ""},
            ListMeta: {
                SelfLink: "",
                ResourceVersion: "",
                Continue: "",
                RemainingItemCount: nil,
            },
            Status: "Failure",
            Message: "Operation cannot be fulfilled on clusterversions.config.openshift.io \"version\": the object has been modified; please apply your changes to the latest version and try again",
            Reason: "Conflict",
            Details: {
                Name: "version",
                Group: "config.openshift.io",
                Kind: "clusterversions",
                UID: "",
                Causes: nil,
                RetryAfterSeconds: 0,
            },
            Code: 409,
        },
    }
occurred
failed: (1.8s) 2026-04-23T17:56:36 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (1.7s) 2026-04-23T17:56:39 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (1.7s) 2026-04-23T17:56:45 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (1.8s) 2026-04-23T17:56:48 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (1.7s) 2026-04-23T17:56:52 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (1.7s) 2026-04-23T17:56:55 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (1.7s) 2026-04-23T17:56:59 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

started: 0/1/1 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

passed: (1.7s) 2026-04-23T17:57:02 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

Flaky tests:

[Jira:"Cluster Version Operator"] cluster-version-operator should create proposals

/test e2e-agnostic-ovn-techpreview-serial-2of3

@hongkailiu hongkailiu changed the title [WIP]OTA-1966: Init the Proprosal Lifecycle Controller OTA-1966: Init the Proprosal Lifecycle Controller Apr 23, 2026
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 23, 2026
@hongkailiu
Copy link
Copy Markdown
Member Author

hypershift is broken?

/test e2e-hypershift-conformance

@hongkailiu
Copy link
Copy Markdown
Member Author

https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1381/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-ovn-techpreview-serial-2of3/2047396599755182080

# https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1381/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-ovn-techpreview-serial-2of3/2047396599755182080/artifacts/e2e-agnostic-ovn-techpreview-serial/openshift-e2e-test/artifacts/e2e.log

started: 0/1/28 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"
passed: (2.2s) 2026-04-23T21:08:25 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

back to normal. run again.

/test e2e-agnostic-ovn-techpreview-serial-2of3

@hongkailiu
Copy link
Copy Markdown
Member Author

This one is also normal.

$ curl -s https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1381/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-ovn-techpreview-serial-2of3/2047474051575713792/artifacts/e2e-agnostic-ovn-techpreview-serial/openshift-e2e-test/artifacts/e2e.log | grep proposal
started: 0/1/28 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"
passed: (700ms) 2026-04-24T02:12:23 "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals"

/test e2e-agnostic-ovn-techpreview-serial-2of3

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Apr 24, 2026

@hongkailiu: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-hypershift-conformance 3d77f7f link true /test e2e-hypershift-conformance
ci/prow/e2e-hypershift 3d77f7f link true /test e2e-hypershift

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@hongkailiu hongkailiu mentioned this pull request Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants