Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion core/workflows/create_implementation_from_issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from datetime import datetime, timedelta, timezone
from pathlib import Path
from textwrap import dedent
from textwrap import dedent, indent
from typing import Any, Mapping, TypedDict

from github.Repository import Repository
Expand All @@ -37,6 +37,7 @@
WorkflowProgressComment,
)
from oz.oz_client import skill_file_path
from .repo_verification import format_repo_scoped_verification_section

WORKFLOW_NAME = "create-implementation-from-issue"
IMPLEMENT_SPECS_SKILL = "implement-specs"
Expand Down Expand Up @@ -70,6 +71,15 @@ def build_create_implementation_prompt(
Used by the webhook dispatch path to feed the implementation agent
the issue/spec context and required handoff contract.
"""
verification_section = indent(
format_repo_scoped_verification_section(
target_repo_full_name=f"{owner}/{repo}",
target_ref=target_branch,
output_artifact="pr-metadata.json",
output_summary_location="`implementation_summary.md` and the `pr_summary` field of `pr-metadata.json`",
),
" ",
)
return dedent(
f"""
Create an implementation update for GitHub issue #{issue_number} in repository {owner}/{repo}.
Expand All @@ -96,6 +106,7 @@ def build_create_implementation_prompt(
- If that branch already exists, fetch it and continue from it. Otherwise create it from `{default_branch}`.
- Align the implementation with the plan context above when present.
- Run the most relevant validation available in the repository.
{verification_section}
- If you produce changes, write `pr-metadata.json` at the repository root containing a JSON object with these required fields:
- `branch_name`: the branch you pushed to. You may customize it by appending a short descriptive slug to the default (e.g. `{target_branch}-add-retry-logic`), but it must start with `{target_branch}`.
- `pr_title`: a conventional-commit-style PR title derived from the actual changes (e.g. `feat: add retry logic for transient API failures`).
Expand Down
33 changes: 33 additions & 0 deletions core/workflows/repo_verification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from __future__ import annotations


def format_repo_scoped_verification_section(
*,
target_repo_full_name: str,
target_ref: str,
target_sha: str = "",
output_artifact: str,
output_summary_location: str,
) -> str:
"""Return prompt text requiring verification against an explicit repo/ref target."""
target_repo = target_repo_full_name.strip() or "the target repository"
ref = target_ref.strip() or "the target ref"
sha = target_sha.strip()
lines = [
"Repository-Scoped Verification (required before final output):",
"- Verify against this target, not whatever repository happens to be checked out locally:",
f" - Target repository: `{target_repo}`",
f" - Target ref/branch: `{ref}`",
]
if sha:
lines.append(f" - Target commit SHA: `{sha}`")
lines.extend(
[
f" - Final artifact that must include verification status: `{output_artifact}`",
f"- Before producing or uploading `{output_artifact}`, make sure your working tree is the target repository/ref above. If the current checkout is missing, wrong, or stale, fetch or clone `https://github.com/{target_repo}.git` and check out the target ref or SHA before validating.",
"- Detect the most appropriate build, test, lint, format, or sanity commands from the target repository's own files and documentation. Prefer repository-defined scripts or workflows over invented commands, and do not hard-code behavior for any one repository name.",
"- Run the selected checks against the target repository state after any generated changes have been applied. If no reliable checks can be inferred, do not present the result as fully verified; explicitly report that verification could not be performed and why.",
f"- Record the verification target, all commands attempted, and each pass/fail/skipped status in {output_summary_location}. Failed or unavailable checks must be visible in the final Oz output.",
]
)
return "\n".join(lines)
11 changes: 11 additions & 0 deletions core/workflows/respond_to_pr_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
split_repo_full_name,
WorkflowProgressComment,
)
from .repo_verification import format_repo_scoped_verification_section

WORKFLOW_NAME = "respond-to-pr-comment"
FETCH_CONTEXT_SCRIPT = ".agents/skills/implement-specs/scripts/fetch_github_context.py"
Expand Down Expand Up @@ -371,6 +372,15 @@ def build_pr_comment_prompt(context: Mapping[str, Any]) -> str:
metadata_branch_line = f"`branch_name`: the branch you pushed to (use `{head_branch}` exactly)."
metadata_requirement_line = "If your changes materially change what this PR contains (for example, adding implementation code on top of a PR that previously only contained spec changes, or otherwise substantially broadening or narrowing the PR's scope), write `pr-metadata.json` at the repository root containing a JSON object with these required fields so the workflow can refresh the PR title and body:"
branch_instructions = indent(branch_instructions, " ")
verification_section = indent(
format_repo_scoped_verification_section(
target_repo_full_name=agent_push_repo_full_name,
target_ref=agent_push_branch,
output_artifact="pr-metadata.json when it is required, otherwise implementation_summary.md or your final summary",
output_summary_location="`implementation_summary.md`, `pr-metadata.json` when present, and any final status summary",
),
" ",
)
return dedent(
f"""\
Make changes for pull request #{pr_number} in repository {owner}/{repo}.
Expand Down Expand Up @@ -398,6 +408,7 @@ def build_pr_comment_prompt(context: Mapping[str, Any]) -> str:
{branch_instructions}
- Align any implementation changes with the plan context above when present.
- Run the most relevant validation available in the repository.
{verification_section}
- If no implementation diff is warranted, do not push the branch.

PR Description Refresh:
Expand Down
29 changes: 27 additions & 2 deletions core/workflows/review_pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import fnmatch
import logging
from pathlib import Path
from textwrap import dedent
from textwrap import dedent, indent
from typing import Any, Mapping, TypedDict
from github.File import File
from github.GithubException import GithubException
Expand Down Expand Up @@ -35,6 +35,7 @@
format_stakeholders_for_prompt,
load_stakeholders_from_repo,
)
from .repo_verification import format_repo_scoped_verification_section

WORKFLOW_NAME = "review-pull-request"

Expand Down Expand Up @@ -501,6 +502,8 @@ class ReviewContext(TypedDict):
pr_body: str
base_branch: str
head_branch: str
head_repo_full_name: str
head_sha: str
trigger_source: str
requester: str
focus_line: str
Expand Down Expand Up @@ -583,6 +586,11 @@ def gather_review_context(
"""
pr = github.get_pull(pr_number)
pr_files = list(pr.get_files())
head_repo_full_name = (
str(getattr(getattr(pr.head, "repo", None), "full_name", "") or "")
or f"{owner}/{repo}"
)
head_sha = str(getattr(pr.head, "sha", "") or "")
changed_files = [str(file.filename) for file in pr_files]
issue_number = resolve_issue_number_for_pr(
github, owner, repo, pr, changed_files
Expand Down Expand Up @@ -670,6 +678,8 @@ def gather_review_context(
pr_body=str(pr.body or ""),
base_branch=str(pr.base.ref),
head_branch=str(pr.head.ref),
head_repo_full_name=head_repo_full_name,
head_sha=head_sha,
trigger_source=trigger_source,
requester=str(requester or ""),
focus_line=focus_line,
Expand Down Expand Up @@ -706,6 +716,19 @@ def build_review_prompt_for_dispatch(context: Mapping[str, Any]) -> str:
if spec_context_text
else "Spec Context: No approved or repository spec context was found for this PR.\n"
)
verification_section = indent(
format_repo_scoped_verification_section(
target_repo_full_name=str(
context.get("head_repo_full_name")
or f"{context['owner']}/{context['repo']}"
),
target_ref=str(context.get("head_branch") or ""),
target_sha=str(context.get("head_sha") or ""),
output_artifact=_REVIEW_OUTPUT_FILENAME,
output_summary_location="the top-level `body` field of `review.json`",
),
" ",
)
prompt = dedent(
f"""
Review pull request #{context['pr_number']} in repository {context['owner']}/{context['repo']}.
Expand All @@ -728,13 +751,15 @@ def build_review_prompt_for_dispatch(context: Mapping[str, Any]) -> str:
- Use the repository's local `{context['skill_name']}` skill as the base workflow.
- {context['supplemental_skill_line']}
- You are running in a cloud environment dispatched by the Vercel control plane. The PR description, annotated diff, and (when available) spec context are inlined below — read them directly instead of fetching anything from GitHub or running the spec-context helper.
- Do not run `git fetch`, `git checkout`, `gh`, ad-hoc GitHub API calls, or the spec-context helper from this run. The control plane already gathered the GitHub-backed context and this run does not receive `GH_TOKEN`.
- Do not run `gh`, ad-hoc GitHub API calls, or the spec-context helper from this run. The control plane already gathered the GitHub-backed PR context and this run does not receive `GH_TOKEN`. Git commands are allowed only as needed to access the target repository/ref for the repo-scoped verification step below.
- Only include comments for files and lines that exist in the inlined PR diff. Every inline comment must map to an explicit `[NEW:n]`, `[OLD:n]`, or `[OLD:n,NEW:m]` annotation from the inlined diff. If feedback does not map to a diff file or commentable diff line, put it in top-level `body` instead of `comments`.
- Before validating, write the inlined PR diff exactly to `pr_diff.txt` so the bundled `validate_review_json.py` script can compare `review.json` against the same annotated diff you reviewed.
- Run `python3 .agents/skills/review-pr/scripts/validate_review_json.py --review-json review.json --diff pr_diff.txt` after creating `review.json`, or locate the bundled `validate_review_json.py` under the packaged `review-pr` skill directory and run that copy. Fix every reported error before upload.
- Do not post the final review directly.
- After you create and validate `review.json`, upload it as an artifact via `oz artifact upload {_REVIEW_OUTPUT_FILENAME}` (or `oz-preview artifact upload {_REVIEW_OUTPUT_FILENAME}` if the `oz` CLI is not available). Either CLI is acceptable — use whichever one is installed in the environment. The subcommand is `artifact` (singular) on both CLIs; do not use `artifacts`.

{verification_section}

PR Description (inline):
----------------
{context['pr_description_text']}
Expand Down
86 changes: 86 additions & 0 deletions tests/test_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
build_pr_comment_prompt,
gather_pr_comment_context,
)
from workflows.create_implementation_from_issue import build_create_implementation_prompt
from workflows.review_pr import build_review_prompt_for_dispatch
from workflows.verify_pr_comment import build_verification_prompt


Expand Down Expand Up @@ -67,6 +69,90 @@ def test_verify_prompt_uses_global_repo_arg_before_subcommand(self) -> None:
)
self.assertNotIn("fetch_github_context.py pr --repo", prompt)

class RepoScopedVerificationPromptTest(unittest.TestCase):
def test_review_prompt_requires_verifying_pr_head_repo_and_ref(self) -> None:
prompt = build_review_prompt_for_dispatch(
{
"owner": "warpdotdev",
"repo": "warp",
"pr_number": 88,
"pr_title": "feat: update review flow",
"pr_body": "body",
"base_branch": "main",
"head_branch": "feature/review-flow",
"head_repo_full_name": "warpdotdev/warp",
"head_sha": "abc123",
"trigger_source": "pull_request",
"focus_line": "Perform a general review.",
"issue_line": "No associated issue resolved for spec lookup.",
"skill_name": "review-pr",
"supplemental_skill_line": "Also apply security-review-pr.",
"repo_local_section": "",
"non_member_review_section": "",
"pr_description_text": "description",
"pr_diff_text": "diff",
"spec_context_text": "",
}
)

self.assertIn("Repository-Scoped Verification", prompt)
self.assertIn("Target repository: `warpdotdev/warp`", prompt)
self.assertIn("Target ref/branch: `feature/review-flow`", prompt)
self.assertIn("Target commit SHA: `abc123`", prompt)
self.assertIn("the top-level `body` field of `review.json`", prompt)
self.assertNotIn("Do not run `git fetch`, `git checkout`", prompt)
self.assertNotIn("webhook", prompt.lower())

def test_implementation_prompt_uses_target_repository_without_hardcoding(self) -> None:
prompt = build_create_implementation_prompt(
owner="acme",
repo="widgets",
issue_number=439,
issue_title="Run repo-scoped verification",
issue_labels=["enhancement"],
issue_assignees=["oz-agent"],
spec_context_text="No approved or repository spec context was found.",
target_branch="oz-agent/implement-issue-439",
default_branch="main",
implement_specs_skill_path=".agents/skills/implement-specs/SKILL.md",
spec_driven_implementation_skill_path=".agents/skills/spec-driven-implementation/SKILL.md",
implement_issue_skill_path=".agents/skills/implement-issue/SKILL.md",
coauthor_directives="",
)

self.assertIn("Target repository: `acme/widgets`", prompt)
self.assertIn("Target ref/branch: `oz-agent/implement-issue-439`", prompt)
self.assertIn("do not hard-code behavior for any one repository name", prompt)
self.assertIn("commands attempted", prompt)
self.assertNotIn("webhook", prompt.lower())

def test_pr_comment_prompt_verifies_agent_push_repository(self) -> None:
prompt = build_pr_comment_prompt(
{
"owner": "octo",
"repo": "tools",
"pr_number": 12,
"head_branch": "feature",
"head_repo_full_name": "octo/tools",
"base_branch": "main",
"base_repo_full_name": "octo/tools",
"pr_title": "feat: add tool",
"requester": "alice",
"trigger_kind": "conversation",
"trigger_comment_id": 99,
"spec_context_text": "No spec context.",
"coauthor_directives": "",
"branch_strategy": "push-head",
"agent_push_repo_full_name": "octo/tools",
"agent_push_branch": "feature",
}
)

self.assertIn("Target repository: `octo/tools`", prompt)
self.assertIn("Target ref/branch: `feature`", prompt)
self.assertIn("pass/fail/skipped status", prompt)
self.assertNotIn("webhook", prompt.lower())


class PrCommentContextBranchSafetyTest(unittest.TestCase):
def _pr(
Expand Down
Loading