Skip to content

OLS-3712 OLS-3558: Fix RBAC discovery, execution outcome override, and prompt refinement#379

Merged
blublinsky merged 1 commit into
openshift:mainfrom
blublinsky:fix-rbac-execution
Jul 27, 2026
Merged

OLS-3712 OLS-3558: Fix RBAC discovery, execution outcome override, and prompt refinement#379
blublinsky merged 1 commit into
openshift:mainfrom
blublinsky:fix-rbac-execution

Conversation

@blublinsky

@blublinsky blublinsky commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • OLS-3712: Fix read RBAC multi-binding resolution — discoverReaderBinding only stored the first ClusterRoleBinding matching the lightspeed-agent SA, silently dropping others (e.g. cluster-monitoring-view). Replaced with resolveReaderBindings which discovers all matching CRBs, caches them, and adds/removes the per-run SA to every one.

  • OLS-3558: Fix execution false failure — when the execution agent reports success=false but all mutating actions succeeded (only pre-checks or inline verification failed), the controller now overrides the outcome to Succeeded and proceeds to the verification step instead of hard-failing.

  • OLS-3558 (prompts): Remove pre-checks and post-checks from the analysis remediation script (analysis already inspected the cluster; verification is a separate step). Remove inline verification instruction from the execution prompt. Scope RBAC derivation to mutations and subresource access only. Add syntax error recovery and non-blocking failure handling as separate execution rules.

Changes

Area Files
RBAC multi-binding rbac.go, rbac_test.go
Execution outcome override handlers.go, handlers_test.go, state_machine_test.go
Prompt refinement analysis_query.tmpl, execution_query.tmpl
Specs sandbox-execution.md, run-lifecycle.md, how/reconciler.md

Test plan

  • make test — all unit tests pass
  • E2E: deploy operator with updated prompts, run PodDisruptionBudgetLimit scenario from OLS-3712 repro steps — verify execution proceeds to verification instead of false-failing
  • E2E: verify per-run SA gets added to both cluster-reader and cluster-monitoring-view CRBs

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 25, 2026
@openshift-ci-robot

openshift-ci-robot commented Jul 25, 2026

Copy link
Copy Markdown

@blublinsky: This pull request references OLS-3712 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 bug to target the "5.0.0" version, but no target version was set.

This pull request references OLS-3558 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 bug 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
openshift-ci Bot requested review from onmete and raptorsun July 25, 2026 20:38
@openshift-ci

openshift-ci Bot commented Jul 25, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign joshuawilson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@blublinsky, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 33fdf042-e329-4445-8dd6-347e8153f06c

📥 Commits

Reviewing files that changed from the base of the PR and between 3bf255a and 3c22cb3.

📒 Files selected for processing (14)
  • .ai/spec/how/reconciler.md
  • .ai/spec/what/run-lifecycle.md
  • .ai/spec/what/sandbox-execution.md
  • controller/agenticrun/handlers.go
  • controller/agenticrun/handlers_test.go
  • controller/agenticrun/helpers.go
  • controller/agenticrun/rbac.go
  • controller/agenticrun/rbac_test.go
  • controller/agenticrun/reconciler.go
  • controller/agenticrun/state_machine_test.go
  • controller/agenticrun/templates/analysis_query.tmpl
  • controller/agenticrun/templates/execution_query.tmpl
  • hack/quickstart/README.md
  • hack/quickstart/install.sh
📝 Walkthrough

Walkthrough

The controller now classifies execution actions by mutation versus observation, defers eligible failures to verification, manages execution ServiceAccount subjects across all matching cached ClusterRoleBindings, and retries deletion cleanup before removing the RBAC finalizer.

Changes

Execution and RBAC lifecycle

Layer / File(s) Summary
Execution prompts and lifecycle contracts
.ai/spec/what/sandbox-execution.md, .ai/spec/what/run-lifecycle.md, .ai/spec/how/reconciler.md, controller/agenticrun/templates/*
Prompts and specifications define mutation-focused scripts, server dry-runs, syntax-only fixes, mutation-focused RBAC derivation, observation classification, and deferred verification outcomes.
Mutation-aware execution handling
controller/agenticrun/handlers.go, controller/agenticrun/handlers_test.go, controller/agenticrun/state_machine_test.go
handleExecution ignores observation failures when evaluating mutation success, proceeds to verification after successful mutations, and fails runs with failed or absent mutations.
Cached multi-binding RBAC management
controller/agenticrun/rbac.go, controller/agenticrun/rbac_test.go
Reader bindings are discovered and cached across all matching ClusterRoleBinding objects; execution subjects are added and removed idempotently with conflict retries.
Deletion cleanup retry handling
controller/agenticrun/reconciler.go, controller/agenticrun/helpers.go
RBAC cleanup uses annotation-based retries and requeues, then removes the cleanup finalizer after the configured attempt limit.

Sequence Diagram(s)

sequenceDiagram
  participant ExecutionAgent
  participant AgenticRunReconciler
  participant VerificationPhase
  ExecutionAgent->>AgenticRunReconciler: report action outcomes
  AgenticRunReconciler->>AgenticRunReconciler: classify mutation and observation actions
  AgenticRunReconciler->>VerificationPhase: defer outcome when mutations succeeded
  VerificationPhase-->>AgenticRunReconciler: return final verification result
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: RBAC discovery, execution outcome override, and prompt/spec refinements.
Description check ✅ Passed The description is directly related to the RBAC, execution, and prompt/spec updates in the changeset.
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.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/agenticrun/rbac.go`:
- Around line 158-159: In controller/agenticrun/rbac.go, update both retry paths
at lines 158-159 and 209-210 to retain the last conflict returned by c.Update
and wrap it in the final error using the %w format with the existing
ErrAddReaderSubject context, preserving Kubernetes error classification and
diagnostics.
- Around line 166-169: The error handling after resolveReaderBindings in the
cleanup flow must not treat every discovery failure as success. Suppress only
the typed “no bindings remain” condition, while propagating forbidden, timeout,
and other errors; retain removeSubjectFromBinding’s existing IsNotFound handling
for bindings deleted during cleanup.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e16719ab-cc84-419a-9a1c-8328fe2544d5

📥 Commits

Reviewing files that changed from the base of the PR and between 401d19e and 217d762.

📒 Files selected for processing (10)
  • .ai/spec/how/reconciler.md
  • .ai/spec/what/run-lifecycle.md
  • .ai/spec/what/sandbox-execution.md
  • controller/agenticrun/handlers.go
  • controller/agenticrun/handlers_test.go
  • controller/agenticrun/rbac.go
  • controller/agenticrun/rbac_test.go
  • controller/agenticrun/state_machine_test.go
  • controller/agenticrun/templates/analysis_query.tmpl
  • controller/agenticrun/templates/execution_query.tmpl
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift/lightspeed-agentic-sandbox (manual)

Comment on lines 158 to 159
}
return fmt.Errorf("%s: conflict after retries", ErrAddReaderSubject)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Preserve the final conflict cause after retry exhaustion.

Store and wrap the last conflict error so callers retain Kubernetes error classification and diagnostics.

  • controller/agenticrun/rbac.go#L158-L159: retain the last c.Update conflict and wrap it with %w.
  • controller/agenticrun/rbac.go#L209-L210: retain the last c.Update conflict and wrap it with %w.

As per coding guidelines, errors should be wrapped with fmt.Errorf("%s: %w", …).

📍 Affects 1 file
  • controller/agenticrun/rbac.go#L158-L159 (this comment)
  • controller/agenticrun/rbac.go#L209-L210
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/agenticrun/rbac.go` around lines 158 - 159, In
controller/agenticrun/rbac.go, update both retry paths at lines 158-159 and
209-210 to retain the last conflict returned by c.Update and wrap it in the
final error using the %w format with the existing ErrAddReaderSubject context,
preserving Kubernetes error classification and diagnostics.

Source: Coding guidelines

Comment thread controller/agenticrun/rbac.go
@blublinsky
blublinsky force-pushed the fix-rbac-execution branch from 217d762 to 3bf255a Compare July 26, 2026 08:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
controller/agenticrun/state_machine_test.go (1)

628-641: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the mutation-aware execution branches.

This test only verifies the no-mutation hard-failure path. Add cases for Success=false with a successful mutation, a failed mutation, and failed observation actions; assert the resulting phase and status outcome. Run the unit suite with make test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/agenticrun/state_machine_test.go` around lines 628 - 641, The test
coverage around TestManualApproval_ExecutionSuccessFalse_NoMutations_Fails only
exercises the no-mutation hard-failure path. Add cases for execution
Success=false with a successful mutation, a failed mutation, and failed
observation actions, asserting each resulting phase and status outcome according
to the state machine behavior. Run the unit suite with make test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controller/agenticrun/handlers.go`:
- Around line 337-341: Update the execution-result handling around
hasMutationSuccess so only mutations with ActionOutcomeSucceeded can override
execResult.Success=false and proceed to verification. Replace the broad
non-failed outcome check with an explicit ActionOutcomeSucceeded comparison, and
apply the same explicit comparison in the mutation scan site to exclude skipped,
pending, or unknown outcomes.
- Around line 778-785: Update isObservationAction and the parse/status boundary
used by hasMutationSuccess to validate ExecutionAction.Type against an explicit
allowlist of recognized observation and mutation categories. Ensure unknown or
misspelled action types are rejected or ignored and cannot fall through as
mutating actions that override workflow status; preserve existing behavior for
recognized types.

---

Nitpick comments:
In `@controller/agenticrun/state_machine_test.go`:
- Around line 628-641: The test coverage around
TestManualApproval_ExecutionSuccessFalse_NoMutations_Fails only exercises the
no-mutation hard-failure path. Add cases for execution Success=false with a
successful mutation, a failed mutation, and failed observation actions,
asserting each resulting phase and status outcome according to the state machine
behavior. Run the unit suite with make test.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f604f6f4-2b80-41f5-894c-e14eee469acc

📥 Commits

Reviewing files that changed from the base of the PR and between 217d762 and 3bf255a.

📒 Files selected for processing (12)
  • .ai/spec/how/reconciler.md
  • .ai/spec/what/run-lifecycle.md
  • .ai/spec/what/sandbox-execution.md
  • controller/agenticrun/handlers.go
  • controller/agenticrun/handlers_test.go
  • controller/agenticrun/helpers.go
  • controller/agenticrun/rbac.go
  • controller/agenticrun/rbac_test.go
  • controller/agenticrun/reconciler.go
  • controller/agenticrun/state_machine_test.go
  • controller/agenticrun/templates/analysis_query.tmpl
  • controller/agenticrun/templates/execution_query.tmpl
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift/lightspeed-agentic-sandbox (manual)
🚧 Files skipped from review as they are similar to previous changes (8)
  • controller/agenticrun/templates/execution_query.tmpl
  • .ai/spec/what/sandbox-execution.md
  • controller/agenticrun/templates/analysis_query.tmpl
  • controller/agenticrun/handlers_test.go
  • .ai/spec/what/run-lifecycle.md
  • .ai/spec/how/reconciler.md
  • controller/agenticrun/rbac.go
  • controller/agenticrun/rbac_test.go

Comment thread controller/agenticrun/handlers.go
Comment on lines +778 to +785
func isObservationAction(actionType string) bool {
switch actionType {
case "pre-check", "post-check", "verification", "check", "wait":
return true
default:
return false
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate handlers.go and relevant symbols =="
fd -a 'handlers\.go$' . | sed 's#^\./##'
echo

echo "== File outline =="
ast-grep outline controller/agenticrun/handlers.go || true
echo

echo "== Relevant isObservationAction and hasMutationSuccess usages =="
rg -n "isObservationAction|hasMutationSuccess|handleExecution|ActionType|Observation|mutation|Success" controller/agenticrun -C 3
echo

echo "== Read relevant handlers.go sections =="
sed -n '720,830p' controller/agenticrun/handlers.go
echo
sed -n '830,960p' controller/agenticrun/handlers.go

Repository: openshift/lightspeed-agentic-operator

Length of output: 50393


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== handlers.go relevant implementation =="
sed -n '759,800p' controller/agenticrun/handlers.go
echo

echo "== all ExecutionAction Type usages =="
rg -n "ExecutionAction|ExecutionPlan|MutationPlan|proposed action|ActionType|propose|type.*action|mutation|check|verification|wait|pre-check|post-check" --glob '*.go' api controller | sed -n '1,240p'
echo

echo "== schema constants around action types =="
sed -n '1,100p' controller/agenticrun/schemas.go
echo

Repository: openshift/lightspeed-agentic-operator

Length of output: 35662


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== executionResult schema/type definitions =="
sed -n '40,105p' api/v1alpha1/agenticrun_status_types.go
sed -n '280,340p' api/v1alpha1/agenticrun_analysis_types.go

echo
echo "== execution query/action type mentions =="
rg -n -C 3 "'mutation'|\"mutation|mutation|ActionOutcome|ActionOutcomeFromBool" controller/agenticrun templates -g '*.go' -g '*.tmpl'

echo
echo "== deterministic behavior probe for isObservationAction logic =="
python3 - <<'PY'
observation = {"pre-check", "post-check", "verification", "check", "wait"}
def is_observation(action_type: str) -> bool:
    return action_type in observation
def has_mutation_success(actions):
    found = False
    for action in actions:
        if is_observation(action["type"]):
            continue
        if action["outcome"] == "Failed":
            return False
        found = True
    return found

cases = [
    ("unrecognized action", [{"type": "dry-run", "outcome": "Succeeded"}]),
    ("typo observation", [{"type": "pre_check", "outcome": "Failed"}]),
    ("unknown succeeded only", [{"type": "unknown", "outcome": "Succeeded"}]),
    ("unknown failed only", [{"type": "unknown", "outcome": "Failed"}]),
]
for name, actions in cases:
    print(name, "hasMutationSuccess=", has_mutation_success(actions))
PY

Repository: openshift/lightspeed-agentic-operator

Length of output: 40415


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== execution action type schema enum/values if present =="
python3 - <<'PY'
import json, re, pathlib
for path in pathlib.Path('controller/agenticrun').glob('*.go'):
    text = path.read_text()
    # Extract JSONRawMessage values containing ExecutionAction type schemas/CRD snippets
    for m in re.finditer(r'json\.RawMessage\s*\(`(.*?)`\)', text, flags=re.S):
        s = m.group(1)
        if '"type"' in s and ('executionresult' in str(path).lower() or 'ExecutionAction' in str(path).lower() or 'actionsTaken' in s):
            print('---', path)
            # Print the compacted action object schema if present
            data = json.loads(s)
            for k,v in data.get('properties', {}).items():
                if k == 'commands' or k == 'actions' or k == 'actionsTaken' or k == 'steps':
                    print(k, v)
PY

echo
echo "== read only action constants/type validation =="
rg -n "enum=|ValidationEnum|ProposedAction|ExecutionAction|\"mutation\"|'mutation'|pre-check|post-check|verification|wait|check" -g '*.go' api/v1alpha1 controller/agenticrun | sed -n '1,260p'

echo
echo "== deterministic behavior probe for isObservationAction logic =="
python3 - <<'PY'
observation = {"pre-check", "post-check", "verification", "check", "wait"}
def is_observation(action_type: str) -> bool:
    return action_type in observation
def has_mutation_success(actions):
    found = False
    for action in actions:
        if is_observation(action["type"]):
            continue
        if action["outcome"] == "Failed":
            return False
        found = True
    return found

cases = [
    ("unrecognized action", [{"type": "dry-run", "outcome": "Succeeded"}]),
    ("typo observation", [{"type": "pre_check", "outcome": "Failed"}]),
    ("unknown succeeded only", [{"type": "unknown", "outcome": "Succeeded"}]),
    ("unknown failed only", [{"type": "unknown", "outcome": "Failed"}]),
]
for name, actions in cases:
    print(name, "hasMutationSuccess=", has_mutation_success(actions))
PY

Repository: openshift/lightspeed-agentic-operator

Length of output: 30993


Fail closed for unvalidated action types.

ExecutionAction.Type is a free-form string, but hasMutationSuccess() treats any type outside the observation allowlist as a mutating action. An unrecognized or misspelled action can make success=false execution result count as mutation success and override the workflow status. Validate execution action types at parse/status boundary with an allowlist of mutation observation categories, or reject/ignore unknown types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/agenticrun/handlers.go` around lines 778 - 785, Update
isObservationAction and the parse/status boundary used by hasMutationSuccess to
validate ExecutionAction.Type against an explicit allowlist of recognized
observation and mutation categories. Ensure unknown or misspelled action types
are rejected or ignored and cannot fall through as mutating actions that
override workflow status; preserve existing behavior for recognized types.

Source: Path instructions

@blublinsky
blublinsky force-pushed the fix-rbac-execution branch from 3bf255a to 31b3496 Compare July 26, 2026 09:09
@blublinsky
blublinsky force-pushed the fix-rbac-execution branch from 31b3496 to 3c22cb3 Compare July 26, 2026 21:17
@openshift-ci

openshift-ci Bot commented Jul 26, 2026

Copy link
Copy Markdown

@blublinsky: all tests passed!

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.

@blublinsky
blublinsky merged commit f0c9524 into openshift:main Jul 27, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

2 participants