Skip to content

✨ Add AgentPlaybook and AgentPlaybookRun controllers#36

Merged
djzager merged 1 commit into
konveyor:mainfrom
djzager:agentplaybook-controllers
Jul 21, 2026
Merged

✨ Add AgentPlaybook and AgentPlaybookRun controllers#36
djzager merged 1 commit into
konveyor:mainfrom
djzager:agentplaybook-controllers

Conversation

@djzager

@djzager djzager commented Jul 15, 2026

Copy link
Copy Markdown
Member

Implements the two remaining controllers from ADR 0001 for sequential multi-stage agent orchestration.

What this does

AgentPlaybook controller — validates that all stages' referenced Agents exist and are Ready. Watches Agent changes via field index for automatic re-reconciliation. Sets a Ready condition on the playbook.

AgentPlaybookRun controller — orchestrates sequential stage execution:

  • Creates one AgentRun per stage, advancing only when the current stage succeeds
  • Uses deterministic AgentRun naming (<playbookrun>-<stage>) with AlreadyExists handling for idempotency on status-patch conflicts (follows the Tekton pattern)
  • Forwards params, models, env, envFrom from the playbook run spec to each stage's AgentRun
  • Passes the playbook's Guide field as KONVEYOR_PLAYBOOK_INSTRUCTIONS env var — the harness decides how to present it alongside the Agent prompt and stage instructions
  • Watches owned AgentRuns for status changes
  • Watches AgentPlaybook for readiness changes (unblocks runs waiting on PlaybookNotReady)
  • Tracks per-stage status (Pending, Running, Succeeded, Failed) with the AgentRun name
  • Fails the entire playbook run if any stage fails; subsequent stages are never started

Bugfix included

Fixes the Sandbox finished-reason constant: "Succeeded""PodSucceeded" to match Agent Sandbox v0.5.0's SandboxReasonPodSucceeded. Without this fix, successful AgentRuns would be incorrectly marked as failed.

Known gaps (intentional for POC)

  • Shared branch management — the controller does not generate or enforce a shared branch across stages. The caller supplies a branch as a param on the playbook run, and the controller forwards it to every stage. Convention-based, not controller-enforced.
  • No param validation against stage Agents — playbook-run params are forwarded wholesale to every stage. If a stage's Agent doesn't declare a param, validation happens at the AgentRun level (existing behavior).
  • No per-stage model overrides — all stages receive the same model selections from the playbook run spec.

Testing

  • AgentPlaybook: missing Agent → Ready=False; all Agents Ready → Ready=True
  • AgentPlaybookRun: missing playbook → Phase=Failed; sequential two-stage execution with simulated success → advances stages → Phase=Succeeded; stage failure → entire run fails, subsequent stages never started
  • All existing tests continue to pass
  • make test: 78% coverage, go vet clean

Design context

This controller is deliberately simple — sequential execution with opaque param forwarding. The architecture preserves optionality for:

  • OpenShell integration — provider/credential management moves to OpenShell, the controller gets simpler
  • Tekton backend — if we need conditionals, parallel stages, or retries, the AgentPlaybookRun could generate Tekton PipelineRuns instead of creating AgentRuns directly
  • Custom Task bridge — our AgentRun creation logic maps directly to a Tekton Custom Task controller

Closes #42

Summary by CodeRabbit

  • New Features
    • Added AgentPlaybook readiness tracking based on referenced stage Agents.
    • Added AgentPlaybookRun orchestration for ordered, stage-by-stage execution with forwarded models/params/env/envFrom and playbook guidance.
    • Deterministic stage AgentRun naming and end-to-end reporting of run/stage status.
  • Bug Fixes
    • Corrected Sandbox success detection to treat PodSucceeded as success for compatibility.
  • Documentation
    • Strengthened AgentPlaybook CRD validation for stage names (length + label-compatible pattern).
  • Chores
    • Updated RBAC and added/expanded controller test coverage.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

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

Walkthrough

Adds AgentPlaybook readiness validation and sequential AgentPlaybookRun orchestration, including deterministic AgentRun creation, status transitions, controller wiring, RBAC, tests, schema validation, and Sandbox success-reason mapping.

Changes

AgentPlaybook orchestration

Layer / File(s) Summary
AgentPlaybook readiness validation
api/v1alpha1/agentplaybook_types.go, config/crd/bases/konveyor.io_agentplaybooks.yaml, internal/controller/agentplaybook_controller.go, internal/controller/agentplaybook_controller_test.go
Validates stage names, checks referenced Agents, updates the playbook Ready condition, and watches indexed Agent references.
Sequential AgentRun orchestration
internal/controller/agentplaybookrun_controller.go, internal/controller/agentplaybookrun_controller_test.go, internal/controller/doc.go
Creates deterministic stage-specific AgentRuns, forwards run inputs and playbook instructions, tracks stage phases, and completes or fails the overall run.
Controller integration and compatibility
cmd/main.go, config/rbac/role.yaml, internal/controller/suite_test.go, internal/controller/agentrun_controller.go, changes/unreleased/agentplaybook-controllers.yaml
Registers both controllers, grants resource and status access, includes them in envtest setup, documents Sandbox success-reason compatibility, and records the feature.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentPlaybookRunReconciler
  participant AgentPlaybook
  participant AgentRun
  participant AgentPlaybookRun
  AgentPlaybookRunReconciler->>AgentPlaybook: read readiness and stages
  AgentPlaybookRunReconciler->>AgentRun: create active stage
  AgentRun-->>AgentPlaybookRunReconciler: return phase
  AgentPlaybookRunReconciler->>AgentPlaybookRun: update stage and overall status
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: savitharaghunathan

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Title check ✅ Passed The title is concise, specific, and accurately summarizes the main change: adding AgentPlaybook and AgentPlaybookRun controllers.
Description check ✅ Passed The description covers the main features, bugfix, limitations, testing, and design context, and it aligns with the repository’s changelog requirement.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@djzager
djzager force-pushed the agentplaybook-controllers branch from 140c3d9 to c48e8cc Compare July 15, 2026 15:37
@djzager djzager changed the title Add AgentPlaybook and AgentPlaybookRun controllers ✨ Add AgentPlaybook and AgentPlaybookRun controllers Jul 15, 2026

@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: 4

🧹 Nitpick comments (3)
internal/controller/agentplaybook_controller_test.go (1)

43-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise the Agent-change watch in this test.

After asserting AgentsNotReady, create and ready the referenced Agent, then assert the existing playbook transitions to AllAgentsReady. Otherwise, the reverse watch/index could be broken while both current scenarios still pass.

🤖 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 `@internal/controller/agentplaybook_controller_test.go` around lines 43 - 65,
The test should exercise the Agent-change watch after the existing
AgentsNotReady assertions: create the referenced Agent named
“nonexistent-agent,” make it ready using the test’s established readiness setup,
then poll the same playbook until its Ready condition becomes True with reason
“AllAgentsReady.” Keep the existing missing-agent assertions and cleanup intact,
and use the controller’s existing Agent fixture/status symbols rather than
inventing a separate readiness path.
internal/controller/agentplaybookrun_controller_test.go (2)

104-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the complete execution-input forwarding contract.

The playbook sets Guide, but the resulting KONVEYOR_PLAYBOOK_INSTRUCTIONS environment variable is not asserted; Env and EnvFrom are not supplied at all. Add these inputs and verify them on the created AgentRuns.

🤖 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 `@internal/controller/agentplaybookrun_controller_test.go` around lines 104 -
158, Extend the AgentPlaybookRun setup and assertions around stageAgentRunName
to cover execution-input forwarding: define representative Env and EnvFrom
values on the playbook run, then assert the created stage AgentRun preserves the
playbook Guide as KONVEYOR_PLAYBOOK_INSTRUCTIONS and forwards both Env and
EnvFrom unchanged. Apply the checks to the created AgentRuns while retaining the
existing stage-specific spec assertions.

51-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a non-ready-to-ready playbook transition test.

The missing-playbook case is terminal, while the sequential case starts after readiness. Neither exercises the indexed AgentPlaybook watch that should resume a waiting run when its playbook becomes Ready.

🤖 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 `@internal/controller/agentplaybookrun_controller_test.go` around lines 51 -
75, Add a controller test alongside the missing-playbook case that creates an
AgentPlaybookRun referencing an existing but initially non-ready AgentPlaybook,
verifies the run remains waiting, then marks the playbook Ready and asserts the
run resumes successfully through the indexed AgentPlaybook watch. Use the
existing test helpers, status condition symbols, and cleanup pattern, and
preserve the terminal missing-playbook test unchanged.
🤖 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 `@internal/controller/agentplaybookrun_controller.go`:
- Around line 323-328: Update the AlreadyExists branch in the AgentRun creation
flow to fetch the existing object and validate that its controller-owner UID and
expected stage labels match the current run before returning agentRunName. If
ownership or labels do not match, return a collision error instead of accepting
the object; preserve the existing deterministic-name behavior for valid prior
creations.
- Around line 102-112: Pin the playbook revision and stage set when execution
begins, then reject any later playbook generation or stage changes before
initializing or advancing stage state. In the readiness check around
playbookReady, require ObservedGeneration to equal playbook.Generation; update
the initialization and findCurrentStageIndex flow to use the pinned stages and
prevent indexing beyond pbRun.Status.Stages.
- Around line 87-96: Set pbRun.Status.CompletionTime in the
errors.IsNotFound(err) branch of the reconciliation flow before calling
patchRunStatus, alongside the Failed phase and Ready condition updates, so this
terminal missing-playbook path records completion immediately.
- Around line 299-307: Update the AgentRun construction in the agent playbook
run reconciliation to use Kubernetes label-safe, hashed/truncated values for
pbRun.Name and stage.Name in labelAgentPlaybookRun and labelStage. Preserve the
full raw names in annotations on the AgentRun so they remain available without
violating label constraints.

---

Nitpick comments:
In `@internal/controller/agentplaybook_controller_test.go`:
- Around line 43-65: The test should exercise the Agent-change watch after the
existing AgentsNotReady assertions: create the referenced Agent named
“nonexistent-agent,” make it ready using the test’s established readiness setup,
then poll the same playbook until its Ready condition becomes True with reason
“AllAgentsReady.” Keep the existing missing-agent assertions and cleanup intact,
and use the controller’s existing Agent fixture/status symbols rather than
inventing a separate readiness path.

In `@internal/controller/agentplaybookrun_controller_test.go`:
- Around line 104-158: Extend the AgentPlaybookRun setup and assertions around
stageAgentRunName to cover execution-input forwarding: define representative Env
and EnvFrom values on the playbook run, then assert the created stage AgentRun
preserves the playbook Guide as KONVEYOR_PLAYBOOK_INSTRUCTIONS and forwards both
Env and EnvFrom unchanged. Apply the checks to the created AgentRuns while
retaining the existing stage-specific spec assertions.
- Around line 51-75: Add a controller test alongside the missing-playbook case
that creates an AgentPlaybookRun referencing an existing but initially non-ready
AgentPlaybook, verifies the run remains waiting, then marks the playbook Ready
and asserts the run resumes successfully through the indexed AgentPlaybook
watch. Use the existing test helpers, status condition symbols, and cleanup
pattern, and preserve the terminal missing-playbook test unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7b7678b-d105-411a-8a45-4139cad6663a

📥 Commits

Reviewing files that changed from the base of the PR and between 8ece07e and dcbcda7.

📒 Files selected for processing (10)
  • changes/unreleased/agentplaybook-controllers.yaml
  • cmd/main.go
  • config/rbac/role.yaml
  • internal/controller/agentplaybook_controller.go
  • internal/controller/agentplaybook_controller_test.go
  • internal/controller/agentplaybookrun_controller.go
  • internal/controller/agentplaybookrun_controller_test.go
  • internal/controller/agentrun_controller.go
  • internal/controller/doc.go
  • internal/controller/suite_test.go

Comment thread internal/controller/agentplaybookrun_controller.go
Comment thread internal/controller/agentplaybookrun_controller.go
Comment on lines +299 to +307
agentRun := &konveyoriov1alpha1.AgentRun{
ObjectMeta: metav1.ObjectMeta{
Name: agentRunName,
Namespace: pbRun.Namespace,
Labels: map[string]string{
labelManagedBy: managedByLabel,
labelAgentPlaybookRun: pbRun.Name,
labelStage: stage.Name,
},

@coderabbitai coderabbitai Bot Jul 15, 2026

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
rg -n -C4 \
  'labelAgentPlaybookRun|labelStage|MaxLength|Pattern|AgentPlaybookStage' \
  internal/controller/agentplaybookrun_controller.go \
  api/v1alpha1/agentplaybook_types.go

Repository: konveyor/agentic-controller

Length of output: 2377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '260,340p' internal/controller/agentplaybookrun_controller.go
printf '\n---\n'
sed -n '1,140p' api/v1alpha1/agentplaybook_types.go
printf '\n---\n'
rg -n -C3 'labelAgentPlaybookRun|labelStage|annotations|Annotation|labels' internal/controller/agentplaybookrun_controller.go api/v1alpha1/agentplaybook_types.go

Repository: konveyor/agentic-controller

Length of output: 7103


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C2 'labelAgentPlaybookRun|labelStage|labelManagedBy|managedByLabel|sanitizeVolumeName|Annotations|annotation' internal api

Repository: konveyor/agentic-controller

Length of output: 6358


Use label-safe values for AgentRun labels. pbRun.Name and stage.Name can exceed Kubernetes label limits or include invalid characters, which can make AgentRun creation fail for otherwise valid playbooks. Hash/truncate the label values and keep the raw names in annotations.

🤖 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 `@internal/controller/agentplaybookrun_controller.go` around lines 299 - 307,
Update the AgentRun construction in the agent playbook run reconciliation to use
Kubernetes label-safe, hashed/truncated values for pbRun.Name and stage.Name in
labelAgentPlaybookRun and labelStage. Preserve the full raw names in annotations
on the AgentRun so they remain available without violating label constraints.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 626eaf7 — added +kubebuilder:validation:MaxLength=63 and +kubebuilder:validation:Pattern to AgentPlaybookStage.Name so it's constrained to valid Kubernetes label values at admission time. CRD regenerated.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '286,325p' internal/controller/agentplaybookrun_controller.go
printf '\n--- AgentPlaybookStage validation ---\n'
rg -n -A12 -B4 'type AgentPlaybookStage struct|Name\s+string' api/v1alpha1/agentplaybook_types.go
printf '\n--- AgentPlaybookRun naming/schema validation ---\n'
rg -n -A20 -B4 'type AgentPlaybookRun struct|type AgentPlaybookRunSpec struct|metadata:|maxLength: 63|pattern:' \
  api/v1alpha1/agentplaybookrun_types.go config/crd/bases/konveyor.io_agentplaybookruns.yaml 2>/dev/null || true

Length of output: 20157


@djzager, the stage-name validation resolves the labelStage half of this issue—thanks.

labelAgentPlaybookRun still uses the raw pbRun.Name, though. AgentPlaybookRun.metadata.name is not limited to 63 characters, so a valid long resource name can still cause child AgentRun creation to fail label validation. Please also derive a label-safe, truncated/hashed value for pbRun.Name (retaining the full name in an annotation if it is needed for lookup).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Valid point about pbRun.Name exceeding 63 chars. In practice K8s object names are max 253 chars but label values are max 63 — same issue exists for labelAgentRun in the existing AgentRun controller. Accepting this as a known limitation for the POC rather than adding truncation/hashing across all controllers in this PR.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@djzager, understood—thanks for clarifying the POC scope. I’ll leave this as an accepted known limitation for this PR, given the same raw-name label pattern already exists in the AgentRun controller.

Would you like me to open a follow-up GitHub issue to standardize label-safe naming across both controllers?


✏️ Learnings added
Learnt from: djzager
Repo: konveyor/agentic-controller PR: 36
File: internal/controller/agentplaybookrun_controller.go:301-309
Timestamp: 2026-07-16T15:42:37.054Z
Learning: For the AgentPlaybook orchestration POC, raw Kubernetes resource names may be used as label values even though names can exceed the 63-character label-value limit. This known limitation also exists in the existing AgentRun controller (`labelAgentRun`) and is intentionally deferred rather than addressed within PR `#36`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread internal/controller/agentplaybookrun_controller.go

@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: 1

🤖 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 `@api/v1alpha1/agentplaybook_types.go`:
- Around line 27-32: The stage-name validation pattern in
api/v1alpha1/agentplaybook_types.go must match the intended Kubernetes
label-value contract: either broaden it to permit uppercase letters and
underscores, or explicitly document and retain the lowercase-only policy.
Regenerate the CRD so the corresponding validation in
config/crd/bases/konveyor.io_agentplaybooks.yaml is synchronized with the source
change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff47920f-5349-406a-bae7-905e63eeb1ee

📥 Commits

Reviewing files that changed from the base of the PR and between dcbcda7 and 626eaf7.

📒 Files selected for processing (3)
  • api/v1alpha1/agentplaybook_types.go
  • config/crd/bases/konveyor.io_agentplaybooks.yaml
  • internal/controller/agentplaybookrun_controller.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/agentplaybookrun_controller.go

Comment thread api/v1alpha1/agentplaybook_types.go
@djzager
djzager force-pushed the agentplaybook-controllers branch from 626eaf7 to acda72a Compare July 16, 2026 15:33

@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 (3)
internal/controller/agentplaybookrun_controller_test.go (3)

64-71: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the missing-playbook completion timestamp.

This test does not cover the newly added terminal timestamp, so a regression could pass unnoticed.

 g.Expect(fetched.Status.Phase).To(Equal(konveyoriov1alpha1.AgentRunPhaseFailed))
+g.Expect(fetched.Status.CompletionTime).NotTo(BeNil())
🤖 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 `@internal/controller/agentplaybookrun_controller_test.go` around lines 64 -
71, Extend the Eventually assertions for the missing-playbook case around
fetched.Status to verify the terminal completion timestamp is populated. Keep
the existing failed phase, Ready condition, and PlaybookNotFound reason
assertions unchanged, and use the status timestamp field introduced by the
controller.

133-143: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover deterministic-name collisions with a foreign AgentRun.

Pre-create expectedStageAName without the playbook run’s controller owner reference, then assert the controller’s documented rejection behavior. This protects the new ownership check from regressions that could adopt an unrelated resource.

🤖 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 `@internal/controller/agentplaybookrun_controller_test.go` around lines 133 -
143, Extend the test around stageAgentRunName and the playbook-run
reconciliation to pre-create an AgentRun named expectedStageAName without the
playbook run’s controller owner reference, then assert the controller rejects
the collision according to its documented behavior instead of adopting the
foreign resource. Keep the existing deterministic-name assertions for the
non-collision path intact.

121-158: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Exercise the complete run-configuration forwarding contract.

Add Env and EnvFrom inputs and assert them on stageARun; also verify the model’s Provider and Model, not only its role. Otherwise those fields can be dropped or miswired without failing this 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 `@internal/controller/agentplaybookrun_controller_test.go` around lines 121 -
158, Add Env and EnvFrom values to the AgentPlaybookRun Spec in the test setup,
then extend the stageARun assertions to verify both fields are forwarded
unchanged. Also assert stageARun.Spec.Models[0].Provider and Model alongside the
existing Role check, covering the complete run-configuration forwarding
contract.
🤖 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 `@internal/controller/agentplaybookrun_controller_test.go`:
- Around line 219-223: Update the successful cleanup path in the test to list
and delete all labeled child AgentRuns before deleting pbRun, reusing the helper
already used by the failure test. Keep the existing playbook and agent cleanup
afterward.
- Around line 170-176: Replace the direct AgentRun status updates at
internal/controller/agentplaybookrun_controller_test.go:170-176, 197-204, and
283-294 with a shared small helper that re-fetches the latest object and retries
Status().Update on conflicts. Use the helper for all three stage success/failure
transitions while preserving their existing status mutations and conditions.

---

Nitpick comments:
In `@internal/controller/agentplaybookrun_controller_test.go`:
- Around line 64-71: Extend the Eventually assertions for the missing-playbook
case around fetched.Status to verify the terminal completion timestamp is
populated. Keep the existing failed phase, Ready condition, and PlaybookNotFound
reason assertions unchanged, and use the status timestamp field introduced by
the controller.
- Around line 133-143: Extend the test around stageAgentRunName and the
playbook-run reconciliation to pre-create an AgentRun named expectedStageAName
without the playbook run’s controller owner reference, then assert the
controller rejects the collision according to its documented behavior instead of
adopting the foreign resource. Keep the existing deterministic-name assertions
for the non-collision path intact.
- Around line 121-158: Add Env and EnvFrom values to the AgentPlaybookRun Spec
in the test setup, then extend the stageARun assertions to verify both fields
are forwarded unchanged. Also assert stageARun.Spec.Models[0].Provider and Model
alongside the existing Role check, covering the complete run-configuration
forwarding contract.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0ada0dc8-a2a4-4266-8bd9-f127a183d355

📥 Commits

Reviewing files that changed from the base of the PR and between 626eaf7 and acda72a.

📒 Files selected for processing (12)
  • api/v1alpha1/agentplaybook_types.go
  • changes/unreleased/agentplaybook-controllers.yaml
  • cmd/main.go
  • config/crd/bases/konveyor.io_agentplaybooks.yaml
  • config/rbac/role.yaml
  • internal/controller/agentplaybook_controller.go
  • internal/controller/agentplaybook_controller_test.go
  • internal/controller/agentplaybookrun_controller.go
  • internal/controller/agentplaybookrun_controller_test.go
  • internal/controller/agentrun_controller.go
  • internal/controller/doc.go
  • internal/controller/suite_test.go
🚧 Files skipped from review as they are similar to previous changes (10)
  • internal/controller/doc.go
  • changes/unreleased/agentplaybook-controllers.yaml
  • cmd/main.go
  • config/rbac/role.yaml
  • api/v1alpha1/agentplaybook_types.go
  • config/crd/bases/konveyor.io_agentplaybooks.yaml
  • internal/controller/agentplaybook_controller.go
  • internal/controller/agentrun_controller.go
  • internal/controller/suite_test.go
  • internal/controller/agentplaybookrun_controller.go

Comment thread internal/controller/agentplaybookrun_controller_test.go Outdated
Comment thread internal/controller/agentplaybookrun_controller_test.go
@djzager
djzager force-pushed the agentplaybook-controllers branch from 97b416c to 370aa56 Compare July 21, 2026 01:16

@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.

♻️ Duplicate comments (2)
internal/controller/agentplaybookrun_controller_test.go (2)

170-176: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Make these AgentRun status writes retry on conflict.

A one-shot Status().Update can race with the live reconciler in envtest, causing these stage success/failure transitions to flake. Introduce a small helper that re-fetches the latest object and retries the status update, and apply it at all three sites.

  • internal/controller/agentplaybookrun_controller_test.go#L170-L176: Replace the one-shot status update for stageARun with a retryable helper or Eventually block.
  • internal/controller/agentplaybookrun_controller_test.go#L197-L204: Apply the same retryable update for stageBRun.
  • internal/controller/agentplaybookrun_controller_test.go#L283-L294: Apply the same retryable update for stageRun in the failure path.
🛠️ Proposed helper and usage

You can define a small helper function at the package or suite level, or inside the test, for example:

func updateAgentRunStatus(name string, updateFn func(*konveyoriov1alpha1.AgentRun)) {
	EventuallyWithOffset(1, func(g Gomega) {
		var run konveyoriov1alpha1.AgentRun
		g.Expect(k8sClient.Get(ctx, types.NamespacedName{Name: name, Namespace: testNamespace}, &run)).To(Succeed())
		updateFn(&run)
		g.Expect(k8sClient.Status().Update(ctx, &run)).To(Succeed())
	}, timeout, interval).Should(Succeed())
}

Then replace the direct updates with calls to this helper. For example, for stage A:

			By("simulating stage-a AgentRun success")
			updateAgentRunStatus(stageARunName, func(run *konveyoriov1alpha1.AgentRun) {
				run.Status.Phase = konveyoriov1alpha1.AgentRunPhaseSucceeded
				meta.SetStatusCondition(&run.Status.Conditions, metav1.Condition{
					Type:   ConditionTypeReady,
					Status: metav1.ConditionTrue,
					Reason: reasonSucceeded,
				})
			})
🤖 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 `@internal/controller/agentplaybookrun_controller_test.go` around lines 170 -
176, Make AgentRun status updates retryable by adding a small helper that
fetches the latest object, applies the status mutation, and retries
Status().Update until it succeeds. Replace the direct updates for stageARun at
internal/controller/agentplaybookrun_controller_test.go:170-176, stageBRun at
internal/controller/agentplaybookrun_controller_test.go:197-204, and stageRun at
internal/controller/agentplaybookrun_controller_test.go:283-294 with this
helper, preserving each success or failure condition.

219-223: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Delete owned AgentRuns on the successful path too.

The failure test explicitly removes child AgentRuns to avoid GC issues, but this successful execution path leaves both stage runs behind. Reusing the list-and-delete logic prevents potential resource leakage or cross-test pollution in envtest.

🧹 Proposed fix
 			By("cleaning up")
+			var runList konveyoriov1alpha1.AgentRunList
+			Expect(k8sClient.List(ctx, &runList,
+				client.InNamespace(testNamespace),
+				client.MatchingLabels{labelAgentPlaybookRun: pbRunName},
+			)).To(Succeed())
+			for i := range runList.Items {
+				Expect(k8sClient.Delete(ctx, &runList.Items[i])).To(Succeed())
+			}
 			Expect(k8sClient.Delete(ctx, pbRun)).To(Succeed())
 			Expect(k8sClient.Delete(ctx, playbook)).To(Succeed())
 			Expect(k8sClient.Delete(ctx, agent)).To(Succeed())
🤖 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 `@internal/controller/agentplaybookrun_controller_test.go` around lines 219 -
223, Update the successful cleanup block in the relevant AgentPlaybookRun test
to list and delete all owned AgentRuns, reusing the failure test’s existing
list-and-delete logic before deleting pbRun, playbook, and agent. Ensure both
stage runs are removed to prevent leftover resources between tests.
🤖 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.

Duplicate comments:
In `@internal/controller/agentplaybookrun_controller_test.go`:
- Around line 170-176: Make AgentRun status updates retryable by adding a small
helper that fetches the latest object, applies the status mutation, and retries
Status().Update until it succeeds. Replace the direct updates for stageARun at
internal/controller/agentplaybookrun_controller_test.go:170-176, stageBRun at
internal/controller/agentplaybookrun_controller_test.go:197-204, and stageRun at
internal/controller/agentplaybookrun_controller_test.go:283-294 with this
helper, preserving each success or failure condition.
- Around line 219-223: Update the successful cleanup block in the relevant
AgentPlaybookRun test to list and delete all owned AgentRuns, reusing the
failure test’s existing list-and-delete logic before deleting pbRun, playbook,
and agent. Ensure both stage runs are removed to prevent leftover resources
between tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 30cb9395-9955-4f66-8cb1-3611b1b5956c

📥 Commits

Reviewing files that changed from the base of the PR and between acda72a and 370aa56.

📒 Files selected for processing (12)
  • api/v1alpha1/agentplaybook_types.go
  • changes/unreleased/agentplaybook-controllers.yaml
  • cmd/main.go
  • config/crd/bases/konveyor.io_agentplaybooks.yaml
  • config/rbac/role.yaml
  • internal/controller/agentplaybook_controller.go
  • internal/controller/agentplaybook_controller_test.go
  • internal/controller/agentplaybookrun_controller.go
  • internal/controller/agentplaybookrun_controller_test.go
  • internal/controller/agentrun_controller.go
  • internal/controller/doc.go
  • internal/controller/suite_test.go
🚧 Files skipped from review as they are similar to previous changes (10)
  • changes/unreleased/agentplaybook-controllers.yaml
  • internal/controller/doc.go
  • config/rbac/role.yaml
  • config/crd/bases/konveyor.io_agentplaybooks.yaml
  • api/v1alpha1/agentplaybook_types.go
  • internal/controller/suite_test.go
  • cmd/main.go
  • internal/controller/agentplaybook_controller.go
  • internal/controller/agentrun_controller.go
  • internal/controller/agentplaybookrun_controller.go

Implement the two remaining controllers from ADR 0001:

AgentPlaybook controller:
- Validates all stages' referenced Agents exist and are Ready
- Watches Agent changes via field index for re-reconciliation
- Sets Ready condition on the playbook

AgentPlaybookRun controller:
- Sequential stage execution: creates one AgentRun per stage
- Uses generateName for AgentRun naming with labels for discoverability
- AlreadyExists handling for idempotency on status-patch conflicts
- Forwards params, models, env, envFrom from playbook run to each
  stage's AgentRun
- Passes playbook Guide as KONVEYOR_PLAYBOOK_INSTRUCTIONS env var
  (harness decides how to present it to the agent runtime)
- Watches owned AgentRuns for status changes
- Watches AgentPlaybook for readiness changes (unblocks runs
  waiting on PlaybookNotReady)
- Tracks per-stage status with phase and AgentRun name
- Uses snapshotted stages in status as source of truth to prevent
  out-of-bounds panics if playbook stages are modified mid-run
- Fails the entire playbook run if any stage fails

Also improves the sandboxFinishedReasonSucceeded constant comment
to note it must match Agent Sandbox's SandboxReasonPodSucceeded.

Signed-off-by: David Zager <david.j.zager@gmail.com>
@djzager
djzager force-pushed the agentplaybook-controllers branch from 370aa56 to 2075299 Compare July 21, 2026 13:31

@ibolton336 ibolton336 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice work — the controller code is idiomatic and follows the repo's existing patterns (patch-from-deep-copy status updates, field-index reverse lookups, deterministic child naming with AlreadyExists + owner-UID verification). The watch wiring looks correct: Owns(AgentRun) drives stage advancement, and the playbook watch unblocks runs stuck on PlaybookNotReady. Requesting changes for one correctness gap; the rest are suggestions.

Blocking

Duplicate stage names aren't rejected, and they break execution silently. AgentPlaybookSpec.Stages only has MinItems=1 — nothing enforces the "unique within the playbook" doc comment on AgentPlaybookStage.Name. With two stages named build, both map to the same deterministic AgentRun name (<run>-build); the second stage's create hits AlreadyExists, passes the ownership check (same owner UID), and instantly "succeeds" by adopting the first stage's AgentRun — so the second stage never actually runs. A CEL rule on the stage list (or +listType=map with name as the key) closes this.

Related edge case: stage names a.b and a-b are distinct per the validation pattern but sanitize to the same AgentRun name, with the same silent-adoption result.

Suggestions

  1. The "snapshot" comment overpromises. The comment in Reconcile says the run "executes the stages that were captured at initialization time," but only stage names and order are snapshotted into status — agentRef and instructions are re-read live from the playbook on every reconcile, so editing a playbook mid-run changes what later stages execute. Either snapshot the full stage spec into status or adjust the comment to document the actual behavior.

  2. Cross-run name collisions loop forever instead of failing terminally. PlaybookRun foo with stage bar-baz and playbook run foo-bar with stage baz both produce AgentRun foo-bar-baz. The not-owned branch in createAgentRunForStage returns an error, which requeues with backoff indefinitely — the losing run is permanently wedged in AgentRunCreationFailed but never reaches Phase=Failed. Since the name is deterministic, the error can never self-heal; it should be a terminal failure rather than a retry.

  3. The description's headline bugfix isn't in the diff. main already has sandboxFinishedReasonSucceeded = "PodSucceeded" — this branch only changes the comment above it. Worth dropping the claim from the PR description and the changelog fragment so the release notes don't advertise a fix that shipped elsewhere.

  4. Guide env var is never asserted in tests. Both tests set Guide, and the sequential test inspects the stage AgentRun spec in detail (params, models, labels), but nothing checks that KONVEYOR_PLAYBOOK_INSTRUCTIONS landed in Spec.Env. One-line assertion worth adding given it's a headline feature. (Also untested: the PlaybookNotReady → Ready unblock path, StageNotFound, and AgentRunDeleted.)

Minor

  • If pbRun.Spec.Env also defines KONVEYOR_PLAYBOOK_INSTRUCTIONS, both entries end up in the AgentRun env (user's entry last). Probably fine as an intentional override hook, but worth a comment.
  • When the playbook goes not-Ready mid-run, the PlaybookNotReady condition overwrites StageRunning while the current stage's AgentRun is still executing — pause/resume works via the watch, but the condition momentarily misrepresents an in-flight stage. Cosmetic.

@djzager
djzager merged commit fff23af into konveyor:main Jul 21, 2026
15 checks passed
ibolton336 added a commit to ibolton336/agentic-controller that referenced this pull request Jul 21, 2026
Read-only slice of konveyor#50. Shim serves agentplaybooks/agentplaybookruns
through the generic READ_ONLY dispatch; agentic-client gains the
AgentPlaybookRun contract types (no duration field — computed from
timestamps; Ready=False/StageRunning documented as the healthy running
state) and list/get methods. UI adds an Agent runs / Playbook runs
toggle, a playbook-runs list (phase, x/y stages, computed duration),
and a detail page with a ProgressStepper stage ladder where each stage
links into the existing RunDetailPage via status.stages[].agentRunName
(never recomputed — names hash-truncate past 63 chars). Stage-owned
AgentRuns in the flat runs list get a parent-playbook badge and a
disabled Delete (deleting one terminally fails the parent, konveyor#36).

Verified against a live 3-stage playbook run (upgrade-run-1) on
minikube with the merged konveyor#36 controllers.

Refs konveyor#50.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ibolton336 ibolton336 mentioned this pull request Jul 21, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5.5: AgentPlaybookRun controller

3 participants