feat: PR automation — opt-in --create-pr flag for both pipelines#75
Merged
Conversation
Adds evolution/core/pr_automation.py with create_pr(), find_git_root(), PRResult, and a small set of private helpers (_branch_name, _atomic_copy, _format_pr_body). The helper packages the four manual steps after a successful evolve — branch off origin/main, copy in the evolved artifact, commit/push, gh pr create — behind one orchestration call. Helper is unused in production this commit; the CLI flag that wires it into evolve_skill/evolve_tool lands separately. Skip / fail paths return structured PRResults with the branch and commit SHA populated where applicable so users can recover from partial progress without re-running.
Address review feedback on c9f2f5c: - Push reason-string formatting into _run_git so every callsite collapses to `return PRResult(status="failed", reason=res)`. Eliminates the inconsistent "git status timed out" vs "git checkout error: <repr>" split that the prior tuple-of-(ok, exception-or-completed) caused. - _branch_name drops the dead `datetime | str` overload; only datetime is ever passed. - _format_pr_body drops the duplicate `### Closed-loop tasks` section (info already in the headline). - Drop the dead `else ""` branch on the delta line (delta is guaranteed non-None inside the surrounding `if baseline and evolved` block).
A successful deploy now optionally branches the source repo, commits the evolved artifact, pushes, and opens a PR via gh — collapsing the prior manual copy/branch/commit/push/PR cycle into one CLI flag. Off by default; --pr-base-branch, --pr-branch-prefix, --pr-draft, and --pr-allow-dirty tune the PR shape. The pr_created outcome lands in gate_decision.json in the same single write as the rest of the decision block so calibration scripts can grep one source of truth.
…ility Address review feedback on 4624757: - The disabled default was {"status": "disabled"} only; the created/ skipped/failed branches build a full 5-field dict. Downstream consumers doing payload["pr_created"]["url"] would KeyError on the default path. Add reason/branch/commit_sha/url = None so the shape is stable across all four statuses. - Tighten the disabled-block tests to assert the full 5-field equality rather than just the status string.
…tring Address review feedback on the wiring commit: - Add disabled_pr_block() and pr_block_from_result() to pr_automation so the 5-field shape lives in one place. Both evolve modules import and use them, eliminating six byte-identical dict literals. - Move evolved_*.md / evolved_manifest.json writes to before the PR hook, guarded by `if growth_pass:`. The post-table block now relies on those writes instead of redoing them, eliminating the duplicate write on the --create-pr deploy path. - EvolutionConfig.create_pr gets a docstring explaining why the field is kept on the dataclass even though no current code path reads it (CLI flag carries the per-run boolean directly via create_pr_flag kwarg; field reserved for future ergonomic-default support).
README PR-review-guardrail section now mentions the new --create-pr opt-in flag, the personal-use-direct-push scope, and the campaign-loop caveat. Memory entries updated: - project_path_e_to_deploy_gate_arc_shipped.md marks PR automation shipped (last item from May 03 phase-1 next-steps doc) - project_pr_automation_shipped.md (new) captures the design constraints that bit during implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the last remaining item from the May 03 phase-1 next-steps doc: auto-generate pull requests against the source skill/tool repository when an evolved artifact passes the deploy gate. Opt-in via
--create-pr. The PR carries the evolved artifact (atomic-write), and a body summarizing the gate decision + metrics.What ships
evolution/core/pr_automation.py:create_pr(...),find_git_root(...), two block-builder helpers (disabled_pr_block,pr_block_from_result), andPRResultdataclass. Stdlib-only (no PyGithub); shells out togit+ghfollowing the existingrun_benchmark_hookpattern.evolve_skillandevolve_tool:--create-pr/--no-create-pr(default off)--pr-base-branch(defaultmain)--pr-branch-prefix(defaultevolve/)--pr-draft(default off)--pr-allow-dirty(default off)gate_decision.json::pr_createdblock records every outcome (status:created/skipped/failed/disabled) with stable 5-field shape so downstream consumers can indexpayload["pr_created"]["url"]unconditionally.build_run_inputsgains a requiredcreate_pr: boolso reruns are reproducible from the artifact alone.Design choices baked into this PR
base_branch, no fork kwargs) is shaped so adding--pr-fork-ownerlater is one kwarg pair, not a refactor.origin/<base>, not local HEAD. Eliminates a class of bugs: stale local main carrying unrelated commits into the PR diff, user on a feature branch committing to the wrong place, working-tree drift polluting the branch.git status --porcelaincheck before any mutation; escape hatch--pr-allow-dirty.tempfile + os.replace(same-filesystem) — matchesMCPManifestSource.apply_evolved's existing atomicity pattern.secrets.token_hex(2)suffix on branch names so back-to-back runs can't collide at second precision.find_git_rootreturnsNone→create_prreturnsskippedwith a clear reason).gate_decision.json:pr_createdis built before the (one and only)write_gate_decisioncall. No re-write.Test plan
tests/core/test_pr_automation.py— the integration test creates a real bare repo + working clone, runs the full orchestration with ONLYgh pr createmocked at the subprocess boundary, asserts the new branch reaches the bare remote and the file content matches.pr_createdblock shape ingate_decision.jsonunder--create-proff + skipped paths.evolve_skill --helpandevolve_tool --helpshow all 5.Smoke deliberately skipped
A real-GitHub end-to-end smoke was not run. The integration test exercises the full real-git path (init bare repo, clone, branch, atomic copy, commit, push) and only mocks the
gh pr createsubprocess invocation. Theghside is a well-known one-shot subprocess call with stderr captured and surfaced on failure — if the user'sgh authworks (verified locally), the real failure modes (auth, missing repo) fail loud rather than silent. Open to running a real-fork smoke as a follow-up if needed.Constraints respected
pr_createdshape across all 4 statuses (no downstream.get()ceremony required)pyproject.tomlunchanged: stdlib + already-present Rich Console