diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..1eeb91c0 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ +## Summary + +- + +## Context + +- Related issue/user request: +- Scope: +- Non-goals: + +## Changes + +- + +## Validation + +Record exact commands and outcomes. Mark items N/A with a reason when they do not apply. + +- [ ] `git diff --check` +- [ ] `cargo fmt --all` +- [ ] `cargo check --workspace` +- [ ] `cargo clippy --workspace --all-targets -- -D warnings` +- [ ] `cargo test --workspace` +- [ ] Targeted/manual checks: +- [ ] Not run: + +## PR Readiness + +- [ ] Diff is limited to the intended files. +- [ ] Generated files were not edited by hand. +- [ ] User-facing docs/help were updated or are not needed. +- [ ] No secrets, credentials, local paths, or unrelated logs are included. +- [ ] Remaining risks or follow-up work are listed above. diff --git a/AGENTS.md b/AGENTS.md index 8c390ff8..a6eea74a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,6 +81,24 @@ When creating issues, add labels that map to the relevant crate(s) — for examp When closing issues via commit, include `fixes #` or `closes #` in the commit message — GitHub closes the issue automatically on merge to main. +## PR Readiness + +Before opening or updating a PR: + +- Re-check the live branch state with `git status --short --branch` and preserve unrelated dirty files. +- Inspect the actual diff with `git diff --stat` and `git diff`; ensure every changed file is intentional for the requested scope. +- Read `.github/PULL_REQUEST_TEMPLATE.md` if present and use it as the PR body structure. +- Choose verification from the touched surface, not a generic checklist. For Rust changes, run the required commands from the Commands section unless the user explicitly narrowed the scope and you can justify a focused subset. For docs/templates-only changes, at minimum run `git diff --check`. +- Include exact commands and outcomes in the PR body. If a check was not run, say why. +- Document context that helps reviewers: related issue/user request, affected crates or docs, user-facing behavior, migration/backcompat notes, and remaining risks. +- Use `gh pr create --body-file ` after previewing the exact body text. Never pass multi-line Markdown directly via `--body`. + +For parallel-agent or batch work: + +- One independently mergeable work unit per branch/PR. Avoid PRs that require another agent's PR to land first. +- Each worker prompt must include the base branch/commit, assigned files or ownership boundary, non-goals, verification recipe, and PR body requirements. +- Each worker final response must include the branch, PR URL, summary, verification evidence, and known gaps. Use a bare `PR: ` line when an orchestrator needs to parse results. + ## Providers Coven Code supports exactly two providers: **Claude** (Anthropic) and **Codex** diff --git a/src-rust/crates/tools/src/bundled_skills.rs b/src-rust/crates/tools/src/bundled_skills.rs index 5b7ad4a8..54890f05 100644 --- a/src-rust/crates/tools/src/bundled_skills.rs +++ b/src-rust/crates/tools/src/bundled_skills.rs @@ -202,12 +202,97 @@ Be direct and actionable. Focus on unblocking, not on explaining concepts."#, user_invocable: true, }, + // ----------------------------------------------------------------------- + // pr-ready + // ----------------------------------------------------------------------- + BundledSkill { + name: "pr-ready", + description: "Prepare or create a review-ready pull request with repo template, context, and verification evidence.", + aliases: &["pr", "pull-request", "ready-pr"], + when_to_use: Some("When the user asks to create or open a PR, make changes PR-ready, or verify a branch before review."), + argument_hint: Some("[scope, issue, or branch]"), + prompt_template: r#"# PR Ready + +Prepare a review-ready pull request. Do not commit, push, or open a PR unless +the user has already asked for that action. If the user only asked whether the +branch is ready, report readiness status instead. + +## Input + +$ARGUMENTS + +## Phase 1: Establish Context + +1. Read repo instructions (`AGENTS.md` plus nested instruction files that apply) + and `.github/PULL_REQUEST_TEMPLATE.md` if present. +2. Inspect git state with `git status --short --branch`; preserve unrelated + dirty files and never stage files outside this task. +3. Identify the base branch, current branch, related issue or PR, requested + scope, affected crates/docs, and non-goals. +4. Review `git diff --stat` and `git diff` so the PR body reflects the real + changed files. + +## Phase 2: Verify + +Choose checks from the touched surface and repository instructions: + +- Rust: run formatter/checks/tests required by the repo, or explain any focused + subset. +- Docs/templates only: run `git diff --check` and any documented docs build or + link check that covers the changed surface. +- UI/TUI: run focused automated tests and a manual or headless smoke path when + available. + +Record each command exactly with pass/fail/blocked status. Do not hide missing +verification; explain the blocker or why the check is out of scope. + +## Phase 3: Compose the PR + +Use the repository PR template when present. If no template exists, use this +minimum body: + +``` +## Summary +- ... + +## Context +- Related issue/user request: +- Scope: +- Non-goals: + +## Changes +- ... + +## Validation +- `command` - PASS/FAIL/BLOCKED, key output +- Not run: ... + +## PR Readiness +- Diff limited to intended files: +- Generated files handled correctly: +- Docs/help updated: +- Remaining risks: +``` + +Before creating or updating the PR, write the body to a temp file, preview the +exact file contents, then use `gh pr create --body-file ` or +`gh pr edit --body-file `. Never pass multi-line Markdown through +`--body`. + +## Final Response + +Report the PR URL, branch, summary, verification evidence, and known gaps. If a +PR was opened, include a bare `PR: ` line so orchestrators can parse it."#, + allowed_tools: None, + user_invocable: true, + }, + // ----------------------------------------------------------------------- // batch // ----------------------------------------------------------------------- BundledSkill { name: "batch", - description: "Research and plan a large-scale change, then execute it in parallel across isolated worktree agents that each open a PR.", + description: "Research and plan a large-scale change, then execute it in parallel across isolated worktree agents that each open a review-ready PR.", aliases: &[], when_to_use: Some("When the user wants to make a sweeping, mechanical change across many files that can be decomposed into independent parallel units."), argument_hint: Some(""), @@ -225,6 +310,9 @@ Enter plan mode, then: 1. **Understand the scope.** Launch subagents to deeply research what this instruction touches. Find all files, patterns, and call sites that need to change. + Read the repo instruction files and existing PR template (`AGENTS.md`, + nested instruction files, `.github/PULL_REQUEST_TEMPLATE.md`) plus relevant + CI workflows so the plan reflects the real review gates. When research touches OpenClaw CLI flows, verify the live command shape with `openclaw agent --help`; avoid obsolete `agent main` subcommands and unsupported OpenClaw persistence flags. @@ -233,11 +321,13 @@ Enter plan mode, then: Each unit must be independently implementable in an isolated git worktree and mergeable on its own without depending on another unit's PR landing first. -3. **Determine the e2e test recipe.** Figure out how a worker can verify its change - actually works end-to-end. If you cannot find a concrete path, ask the user. +3. **Determine the PR readiness contract.** Figure out how each worker can verify + its change end-to-end, which template sections it must fill, which labels or + issue links are relevant, and what evidence reviewers need. If you cannot + find a concrete verification path, ask the user. 4. **Write the plan.** Include: research summary, numbered work units, e2e recipe, - and the exact worker instructions. + PR body requirements, and the exact worker instructions. ## Phase 2: Spawn Workers (After Plan Approval) @@ -245,8 +335,23 @@ Spawn one background agent per work unit using the Agent tool with `isolation: "worktree"` and `run_in_background: true`. Launch them all in a single message block so they run in parallel. Each agent prompt must be fully self-contained. +Each worker prompt must include: + +- Base branch and base commit/sha. +- The exact assigned objective, owned files or boundaries, and non-goals. +- Required context: instruction files, related issues/PRs, template sections, and + code paths the worker must read before editing. +- Verification commands and expected evidence. +- PR creation rules: stage only files changed for the unit, preserve unrelated + dirty work, write the PR body to a temp file, preview it, then use + `gh pr create --body-file `. +- Final response contract: branch, PR URL, summary, verification, risks, and a + bare `PR: ` line. + After each agent finishes, parse the `PR: ` line from its result and render -a status table. When all agents have reported, print a final summary."#, +a status table with unit, branch, PR, verification, and risk/gap columns. When +all agents have reported, print a final summary that identifies ready PRs, +blocked PRs, duplicate work, and any remaining integration order."#, allowed_tools: None, user_invocable: true, }, @@ -554,6 +659,22 @@ mod tests { assert!(skill.prompt_template.contains("options")); } + #[test] + fn pr_ready_skill_enforces_template_and_body_file_contract() { + let skill = find_bundled_skill("pr-ready").unwrap(); + assert!(skill.user_invocable); + assert_eq!(find_bundled_skill("pull-request").unwrap().name, "pr-ready"); + assert!(skill + .prompt_template + .contains(".github/PULL_REQUEST_TEMPLATE.md")); + assert!(skill + .prompt_template + .contains("git status --short --branch")); + assert!(skill.prompt_template.contains("preserve unrelated")); + assert!(skill.prompt_template.contains("gh pr create --body-file")); + assert!(skill.prompt_template.contains("PR: ")); + } + #[test] fn batch_skill_guards_stale_openclaw_research_patterns() { let skill = find_bundled_skill("batch").unwrap(); @@ -564,6 +685,20 @@ mod tests { .contains("unsupported OpenClaw persistence flags")); } + #[test] + fn batch_skill_requires_pr_readiness_contract_for_workers() { + let skill = find_bundled_skill("batch").unwrap(); + assert!(skill + .prompt_template + .contains(".github/PULL_REQUEST_TEMPLATE.md")); + assert!(skill.prompt_template.contains("PR readiness contract")); + assert!(skill + .prompt_template + .contains("Base branch and base commit/sha")); + assert!(skill.prompt_template.contains("gh pr create --body-file")); + assert!(skill.prompt_template.contains("risk/gap columns")); + } + #[test] fn find_case_insensitive() { assert!(find_bundled_skill("SIMPLIFY").is_some());