Skip to content

Commit 762c055

Browse files
committed
full workflow
1 parent 656b0e5 commit 762c055

File tree

4 files changed

+301
-123
lines changed

4 files changed

+301
-123
lines changed

plugins/oape/commands/review.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ The review covers four key modules:
2323

2424
## Arguments
2525

26-
- `$1` (ticket_id): The Jira Ticket ID (e.g., OCPBUGS-12345). **Required.**
26+
- `$1` (ticket_id_or_NO_TICKET): The Jira Ticket ID (e.g., OCPBUGS-12345) **OR** the literal string `NO_TICKET`. **Required.**
27+
- When `NO_TICKET` is provided, the review skips Jira validation and focuses on code quality, safety, and build consistency only.
2728
- `$2` (base_ref): The base git ref to diff against. Defaults to `origin/master`. **Optional.**
2829

2930

@@ -38,11 +39,16 @@ BASE_REF="${2:-origin/master}"
3839
```
3940

4041
### Step 2: Fetch Context
41-
1. **Jira Issue**: Fetch the Jira issue details using curl:
42-
```bash
43-
curl -s "https://issues.redhat.com/browse/$1"
44-
```
45-
Focus on Acceptance Criteria as the primary validation source.
42+
1. **Jira Issue** (skip if `$1` is `NO_TICKET`):
43+
- If `$1` is NOT `NO_TICKET`, fetch the Jira issue details using curl:
44+
```bash
45+
curl -s "https://issues.redhat.com/browse/$1"
46+
```
47+
Focus on Acceptance Criteria as the primary validation source.
48+
- If `$1` IS `NO_TICKET`, skip Jira fetching entirely. The review will focus
49+
on code quality (Modules A-D) without validating against Jira acceptance criteria.
50+
In the Logic Verification module, skip the "Intent Match" check that compares
51+
code against Jira requirements.
4652

4753
2. **Git Diff**: Get the code changes:
4854
```bash
@@ -61,7 +67,7 @@ Apply the following review criteria:
6167
#### Module A: Golang (Logic & Safety)
6268

6369
**Logic Verification (The "Mental Sandbox")**:
64-
- **Intent Match:** Does the code implementation match the Jira Acceptance Criteria? Quote the Jira line that justifies the change.
70+
- **Intent Match:** (Skip if `NO_TICKET` mode) Does the code implementation match the Jira Acceptance Criteria? Quote the Jira line that justifies the change. In `NO_TICKET` mode, verify that the code changes are internally consistent and logically correct.
6571
- **Execution Trace:** Mentally simulate the function.
6672
- *Happy Path:* Does it succeed as expected?
6773
- *Error Path:* If the API fails, does it retry or return an error?
@@ -116,7 +122,7 @@ Returns a JSON report with the following structure, followed by an automatic fix
116122
"simplicity_score": "1-10"
117123
},
118124
"logic_verification": {
119-
"jira_intent_met": true,
125+
"jira_intent_met": true, // Set to null in NO_TICKET mode
120126
"missing_edge_cases": ["List handled edge cases or gaps (e.g., 'Does not handle pod deletion')"]
121127
},
122128
"issues": [

server/agent.py

Lines changed: 169 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
"""
22
Core agent execution logic shared between sync and async endpoints.
3+
4+
Supports both single-command execution and full workflow orchestration.
35
"""
46

57
import json
68
import logging
9+
import os
710
import traceback
11+
import uuid
812
from collections.abc import Callable
913
from dataclasses import dataclass, field
1014
from pathlib import Path
@@ -22,6 +26,7 @@
2226

2327
# Resolve the plugin directory (repo root) relative to this file.
2428
PLUGIN_DIR = str(Path(__file__).resolve().parent.parent / "plugins" / "oape")
29+
TEAM_REPOS_CSV = str(Path(__file__).resolve().parent.parent / "team-repos.csv")
2530

2631
CONVERSATION_LOG = Path("/tmp/conversation.log")
2732

@@ -34,10 +39,125 @@
3439
with open(Path(__file__).resolve().parent / "config.json") as cf:
3540
CONFIGS = json.loads(cf.read())
3641

37-
# Supported commands and their corresponding plugin skill names.
38-
SUPPORTED_COMMANDS = {
39-
"api-implement": "oape:api-implement",
40-
}
42+
# ---------------------------------------------------------------------------
43+
# Workflow system prompt — instructs a single long-running agent session
44+
# to execute the full feature development pipeline.
45+
# ---------------------------------------------------------------------------
46+
47+
WORKFLOW_SYSTEM_PROMPT = """\
48+
You are an OpenShift operator AI feature development agent. Your job is to
49+
execute a complete feature development workflow given an Enhancement Proposal
50+
(EP) PR URL. You MUST follow every phase below **in order**, and you MUST NOT
51+
skip any phase. Stream your progress clearly so the user can follow along.
52+
53+
## Inputs
54+
- EP_URL: the GitHub Enhancement Proposal PR URL (provided in the user prompt).
55+
56+
## Allowed repositories
57+
Read the file {team_repos_csv} to get the list of allowed repositories and
58+
their base branches (CSV columns: product, role, repo_url, base_branch).
59+
60+
## Phase 0 — Detect target repository
61+
1. Fetch the EP PR description using `gh pr view <EP_URL> --json body -q .body`
62+
(or WebFetch the PR page).
63+
2. From the EP content, determine which operator repository it targets.
64+
Match against the repos in {team_repos_csv}.
65+
3. Extract: REPO_SHORT_NAME, REPO_URL, BASE_BRANCH.
66+
- REPO_SHORT_NAME is the last path component of the repo URL without .git
67+
(e.g. "cert-manager-operator").
68+
4. Print a summary: "Detected repo: <REPO_SHORT_NAME>, base branch: <BASE_BRANCH>".
69+
70+
If you cannot determine the repo, STOP and report the failure.
71+
72+
## Phase 1 — Clone repository
73+
1. Run `/oape:init <REPO_SHORT_NAME>` in the current working directory.
74+
2. After init completes, `cd` into the cloned repo directory.
75+
3. Run `git fetch origin` and `git checkout <BASE_BRANCH>` to ensure you are
76+
on the correct base branch.
77+
4. Extract the EP number from EP_URL (the numeric part after /pull/).
78+
79+
## Phase 2 — PR #1: API Types + Tests
80+
1. Create and checkout a new branch: `git checkout -b oape/api-types-<EP_NUMBER>`
81+
2. Run `/oape:api-generate <EP_URL>`.
82+
3. Identify the generated API directory (typically `api/` or a subdirectory).
83+
4. Run `/oape:api-generate-tests <path-to-api-directory>`.
84+
5. Run `make generate && make manifests` (if Makefile targets exist).
85+
6. Run `make build && make test` to verify the code compiles and tests pass.
86+
If build or tests fail, attempt to fix the issues.
87+
7. Run `/oape:review NO_TICKET origin/<BASE_BRANCH>` to review code quality.
88+
The review will auto-fix issues it finds.
89+
8. Run `make build && make test` again after review fixes.
90+
9. Stage all changes: `git add -A`
91+
10. Commit: `git commit -m "oape: generate API types and tests from EP #<EP_NUMBER>"`
92+
11. Push: `git push -u origin oape/api-types-<EP_NUMBER>`
93+
12. Create PR #1:
94+
```
95+
gh pr create \\
96+
--base <BASE_BRANCH> \\
97+
--title "oape: API types and tests from EP #<EP_NUMBER>" \\
98+
--body "Auto-generated API type definitions and integration tests from <EP_URL>"
99+
```
100+
13. Save the PR #1 URL.
101+
102+
## Phase 3 — PR #2: Controller Implementation
103+
1. From the current branch (oape/api-types-<EP_NUMBER>), create a new branch:
104+
`git checkout -b oape/controller-<EP_NUMBER>`
105+
2. Run `/oape:api-implement <EP_URL>`.
106+
3. Run `make generate && make manifests` (if targets exist).
107+
4. Run `make build && make test`. Fix failures if any.
108+
5. Run `/oape:review NO_TICKET origin/<BASE_BRANCH>` to review.
109+
6. Run `make build && make test` again after review fixes.
110+
7. Stage, commit: `git commit -m "oape: implement controller from EP #<EP_NUMBER>"`
111+
8. Push: `git push -u origin oape/controller-<EP_NUMBER>`
112+
9. Create PR #2 (base = the api-types branch so it stacks):
113+
```
114+
gh pr create \\
115+
--base oape/api-types-<EP_NUMBER> \\
116+
--title "oape: controller implementation from EP #<EP_NUMBER>" \\
117+
--body "Auto-generated controller/reconciler code from <EP_URL>"
118+
```
119+
10. Save the PR #2 URL.
120+
121+
## Phase 4 — PR #3: E2E Tests
122+
1. Go back to the api-types branch: `git checkout oape/api-types-<EP_NUMBER>`
123+
2. Create a new branch: `git checkout -b oape/e2e-<EP_NUMBER>`
124+
3. Run `/oape:e2e-generate <BASE_BRANCH>`.
125+
4. Run `/oape:review NO_TICKET origin/<BASE_BRANCH>` to review.
126+
5. Fix any issues, verify build passes.
127+
6. Stage, commit: `git commit -m "oape: generate e2e tests from EP #<EP_NUMBER>"`
128+
7. Push: `git push -u origin oape/e2e-<EP_NUMBER>`
129+
8. Create PR #3 (base = the api-types branch):
130+
```
131+
gh pr create \\
132+
--base oape/api-types-<EP_NUMBER> \\
133+
--title "oape: e2e tests from EP #<EP_NUMBER>" \\
134+
--body "Auto-generated e2e test artifacts from <EP_URL>"
135+
```
136+
9. Save the PR #3 URL.
137+
138+
## Phase 5 — Summary
139+
Output a final summary in this exact format:
140+
141+
```
142+
=== OAPE Workflow Complete ===
143+
144+
Enhancement Proposal: <EP_URL>
145+
Target Repository: <REPO_SHORT_NAME>
146+
Base Branch: <BASE_BRANCH>
147+
148+
PR #1 (API Types + Tests): <PR_1_URL>
149+
PR #2 (Controller): <PR_2_URL>
150+
PR #3 (E2E Tests): <PR_3_URL>
151+
```
152+
153+
## Critical Rules
154+
- NEVER skip a phase. Execute them in order.
155+
- If a phase fails and you cannot recover, STOP and report which phase failed and why.
156+
- Always use `git add -A` before committing to include all generated files.
157+
- The review command with NO_TICKET skips Jira validation and reviews code quality only.
158+
- Use `gh pr create` (not manual URL construction) for creating PRs.
159+
- Do NOT modify the EP or any upstream repository.
160+
"""
41161

42162

43163
@dataclass
@@ -55,37 +175,25 @@ def success(self) -> bool:
55175

56176

57177
async def run_agent(
58-
command: str,
59-
ep_url: str,
178+
prompt: str,
60179
working_dir: str,
180+
system_prompt: str,
61181
on_message: Callable[[dict], None] | None = None,
62182
) -> AgentResult:
63-
"""Run the Claude agent and return the result.
183+
"""Run the Claude agent with an arbitrary prompt and system prompt.
64184
65185
Args:
66-
command: The command key (e.g. "api-implement").
67-
ep_url: The enhancement proposal PR URL.
68-
working_dir: Absolute path to the operator repo.
186+
prompt: The user prompt to send to the agent.
187+
working_dir: Absolute path to the working directory.
188+
system_prompt: The system prompt for the agent.
69189
on_message: Optional callback invoked with each conversation message
70190
dict as it arrives, enabling real-time streaming.
71191
72192
Returns:
73193
An AgentResult with the output or error.
74194
"""
75-
skill_name = SUPPORTED_COMMANDS.get(command)
76-
if skill_name is None:
77-
return AgentResult(
78-
output="",
79-
cost_usd=0.0,
80-
error=f"Unsupported command: {command}. "
81-
f"Supported: {', '.join(SUPPORTED_COMMANDS)}",
82-
)
83-
84195
options = ClaudeAgentOptions(
85-
system_prompt=(
86-
"You are an OpenShift operator code generation assistant. "
87-
f"Execute the {skill_name} plugin with the provided EP URL. "
88-
),
196+
system_prompt=system_prompt,
89197
cwd=working_dir,
90198
permission_mode="bypassPermissions",
91199
allowed_tools=CONFIGS["claude_allowed_tools"],
@@ -97,7 +205,7 @@ async def run_agent(
97205
cost_usd = 0.0
98206

99207
conv_logger.info(
100-
f"\n{'=' * 60}\n[request] command={command} ep_url={ep_url} "
208+
f"\n{'=' * 60}\n[request] prompt={prompt[:120]} "
101209
f"cwd={working_dir}\n{'=' * 60}"
102210
)
103211

@@ -109,7 +217,7 @@ def _emit(entry: dict) -> None:
109217

110218
try:
111219
async for message in query(
112-
prompt=f"/{skill_name} {ep_url}",
220+
prompt=prompt,
113221
options=options,
114222
):
115223
if isinstance(message, AssistantMessage):
@@ -198,3 +306,39 @@ def _emit(entry: dict) -> None:
198306
error=str(exc),
199307
conversation=conversation,
200308
)
309+
310+
311+
async def run_workflow(
312+
ep_url: str,
313+
on_message: Callable[[dict], None] | None = None,
314+
) -> AgentResult:
315+
"""Run the full OAPE feature development workflow.
316+
317+
Creates a temp directory, then launches a single long-running agent session
318+
that executes all phases: init, api-generate, api-generate-tests, review,
319+
PR creation, api-implement, e2e-generate, etc.
320+
321+
Args:
322+
ep_url: The enhancement proposal PR URL.
323+
on_message: Optional streaming callback.
324+
325+
Returns:
326+
An AgentResult with the final summary or error.
327+
"""
328+
job_id = uuid.uuid4().hex[:12]
329+
working_dir = f"/tmp/oape-{job_id}"
330+
os.makedirs(working_dir, exist_ok=True)
331+
332+
system_prompt = WORKFLOW_SYSTEM_PROMPT.format(
333+
team_repos_csv=TEAM_REPOS_CSV,
334+
)
335+
336+
return await run_agent(
337+
prompt=(
338+
f"Execute the full OAPE feature development workflow for this "
339+
f"Enhancement Proposal: {ep_url}"
340+
),
341+
working_dir=working_dir,
342+
system_prompt=system_prompt,
343+
on_message=on_message,
344+
)

0 commit comments

Comments
 (0)