Skip to content

Add opt-in auto-push of agent commits to git remote - #282

Open
zwanski-start wants to merge 1 commit into
AutoForgeAI:masterfrom
zwanski-start:swarm/issue-151-dennis
Open

zwanski-start wants to merge 1 commit into
AutoForgeAI:masterfrom
zwanski-start:swarm/issue-151-dennis

Conversation

@zwanski-start

Copy link
Copy Markdown

Closes #151

Issue #151 asks whether AutoForge could push to a remote automatically when a feature build cycle ends. This PR adds that as an opt-in setting and documents the feature-branch workflow described in the issue.

What changed

  • git_sync.py (new): push_current_branch(project_dir) pushes the current branch to its upstream. If the branch has no upstream, it pushes to origin with --set-upstream. Rules:
    • It never force-pushes and never creates remotes or repos.
    • It skips when the directory is not a repo, when HEAD is detached, or when there is no upstream and no origin.
    • It runs non-interactively (GIT_TERMINAL_PROMPT=0) with a 60s timeout. A lock serializes concurrent pushes from parallel agents.
    • is_auto_push_enabled() reads AUTOFORGE_GIT_AUTO_PUSH first, then the git_auto_push registry setting. Default is off.
  • parallel_orchestrator.py: after a coding agent exits with code 0, it calls the push helper if the setting is on. The result is printed as a [GIT] ... log line, so it shows up in the UI log stream, where existing token redaction applies. Errors are never fatal.
  • Settings API (server/schemas.py, server/routers/settings.py): adds the git_auto_push bool to GET/PATCH.
  • UI: a new Version Control card in the Settings page (SettingsView.tsx) with an "Auto-push to Git Remote" switch. The same toggle is also in SettingsModal.tsx, which is not currently rendered, so it stays in sync with the page as its header comment says. Types and defaults are updated.
  • Docs: a README section "GitHub / Git Remote Integration (Auto-Push)" covers the one-time gh repo create --source=. --push setup and the feature-branch workflow. CLAUDE.md lists the new module and test.

Verification

  • test_git_sync.py (11 tests) uses real temp repos and a local bare remote. It covers: not a repo, no remote, push to origin plus upstream setup, a follow-up push through the upstream, a feature branch, detached HEAD, a rejected non-fast-forward push (it checks the remote is not overwritten), and env var / setting precedence.
  • ruff check . is clean. mypy reports no errors in the touched files (the other errors are pre-existing). test_security.py, test_client.py, test_dependency_resolver.py and test_rate_limit_utils.py all pass.
  • npm run lint shows 0 errors and npm run build succeeds.
  • I ran the server on port 5247 with an isolated HOME and opened Settings in Playwright. The Version Control card renders, and toggling the switch persists (GET /api/settings returns "git_auto_push": true).
  • Not tested: a full agent session that actually pushes to a real remote. The orchestrator hook is a thin call into the tested helper.

Assumptions

  • The push runs after every successful coding session, not after testing agents or the initializer. The first coding session pushes the initializer's commits too.
  • It pushes whatever branch is checked out, so the user controls the branch, as in the issue's feature-branch workflow. Repo creation and PR creation stay manual, since they need user decisions (visibility, naming, review). The README shows the gh commands.
  • Credentials must already work non-interactively (gh auth setup-git, SSH key, or a credential helper).

🤖 Generated with Claude Code

New opt-in 'Auto-push to Git Remote' setting (Settings > Version Control,
or AUTOFORGE_GIT_AUTO_PUSH=1). After each successful coding session the
orchestrator pushes the project's current branch to its upstream, or to
origin with --set-upstream. Never force-pushes, never creates remotes, runs
non-interactively with a timeout, and failures are logged with a [GIT]
prefix without affecting the agent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zwanski-start

Copy link
Copy Markdown
Author

🔍 QA report: ✅ Passed

Tester: Barbara (cubefarm agent, in a fresh session) · Round: 1 · Author: Dennis

Opt-in auto-push works as described and meets issue #151. The push helper never force-pushes, skips safely without an upstream, origin or branch, can't hang on a password prompt, and a password embedded in the remote URL does not appear in its log line. The setting defaults to off, persists through the Settings API and the new Version Control card, and the env var overrides it. Lint, build and the 11 new tests pass; the pytest failures and mypy errors are the same as on master.

Check Details
✅ Issue acceptance: optional push of the current (feature) branch to the remote after a build cycle Called ParallelOrchestrator._maybe_push_to_remote against a temp repo with the setting enabled through the server's registry (isolated HOME). It pushed 'main' to a local bare origin and set the upstream, and a later call on feature branch 'feat/é-ü' pushed that branch.
✅ Safety: no force push, skips without remote or branch, credentials not leaked The 11 new tests cover not-a-repo, no remote, detached HEAD and a rejected non-fast-forward push (remote left unchanged). Manual probe: origin set to https://user:SECRETTOKEN@127.0.0.1:1/x.git gave '[GIT] Auto-push skipped: ... unable to access https://127.0.0.1:1/x.git/', so the password was stripped and the run continued.
✅ Opt-in and env var precedence GET /api/settings defaults to git_auto_push=false. With the setting true, AUTOFORGE_GIT_AUTO_PUSH=0 stopped the push (remote stayed behind).
✅ Hook placement in the orchestrator Runs only in the coding-agent branch of _on_agent_complete when return_code==0, in the per-agent reader thread, wrapped in try/except. Testing agents and stopped (killed) agents don't trigger it. A module lock serializes concurrent pushes. No security.py or localhost-middleware changes.
✅ Settings API validation and persistence PATCH {git_auto_push:'notabool'} returns 422 bool_parsing. PATCH true returns 200 and GET returns true. PATCH {yolo_mode:true} leaves git_auto_push unchanged.
✅ UI: Version Control card in Settings The card renders with the switch off. Clicking it sets git_auto_push=true on the backend, and the switch is still on after a reload. No console errors or warnings.
✅ Responsive layout (390px) With the sidebar expanded, the new switch extends 15px past the viewport edge and the existing YOLO switch ends exactly at it. This comes from the sidebar not collapsing at phone width, not from the PR. With the sidebar collapsed, both switches fit (right edge 341px of 390).
✅ Backend test suite vs master pytest: 21 failed, 155 passed, and the FAILED list is identical to origin/master. test_git_sync.py: 11 passed. test_security.py: all passed.
✅ mypy vs master Same errors as master. The only extra one is venv/bin/activate_this.py, which comes from this worktree's local venv, not from the PR.
✅ Server boot and key routes uvicorn server.main:app booted on 5443; /api/settings and /api/projects returned 200 and the server log had no errors. WebSocket /ws was not checked (code unchanged).
⏭️ UI Playwright e2e The bundled Playwright browser download kept stalling, so the suite ran on system Chromium through a temporary config (deleted afterwards). Exit 0, but all 15 tests skipped themselves because they need a registered project.
⏭️ Real push to GitHub during a live agent session Not done (no live agent or paid API, per the QA rules). The hook was checked by code review and the direct call described above.
🧪 Commands run
Command Result
ruff check . All checks passed
python -m pytest test_git_sync.py -q 11 passed
python -m pytest -q (PR vs origin/master worktree) 21 failed / 155 passed on both; identical FAILED lists
mypy . (PR vs master) Same errors as master; only extra is the local venv/ folder
python test_security.py ALL TESTS PASSED
npm --prefix ui run build tsc -b && vite build succeeded
npm --prefix ui run lint 0 errors, 6 warnings
HOME=<isolated> uvicorn server.main:app --port 5443 + curl GET/PATCH /api/settings, GET /api/projects 200 / 422 (invalid bool) / 200 as expected; setting persisted
python probe.py (direct call to _maybe_push_to_remote on temp repos) Push to bare origin OK; unreachable remote with password in URL reported without the password; env=0 stopped the push; non-ASCII branch pushed
VITE_API_PORT=5443 npx playwright test -c <temp config using /usr/bin/chromium> 15 skipped (need a registered project), exit 0

No browser screenshots were taken in this round.

Posted by cubefarm · merges automatically once GitHub's checks pass

cabana8471-arch pushed a commit to cabana8471-arch/autoforge that referenced this pull request Nov 8, 2026
After a coding session ends successfully, the project's current branch can
be pushed to its git remote, so the agent's commits show up on GitHub
without manual steps.

Adaptations on integration (the agent can run any git command in the
project, and the push runs in the orchestrator, outside its sandbox):
- per project and off by default (Settings > Version Control for the
  selected project) instead of a global toggle / AUTOFORGE_GIT_AUTO_PUSH;
  turning it on stores the remote's current URL, which the page shows;
- every push goes explicitly to the stored URL (full refspec, no force) and
  is refused when `git remote get-url <remote>` differs from it;
- refused when the repo-local config sets url.* (insteadOf/pushInsteadOf
  rewrite even an explicit URL), core.sshCommand/askpass/hooksPath/
  fsmonitor/gitProxy, credential.*, include.*/includeIf.*, http.* or gpg.*;
- hooks never run (-c core.hooksPath=/dev/null, --no-verify), push.gpgSign
  and submodule recursion are off;
- the push runs in a background thread after _signal_agent_completed();
- git_sync.py added to package.json files;
- tests: changed remote URL, stored URL used over a planted pushurl, each
  unsafe key, a pre-push hook writing a marker does not run, no force, the
  orchestrator does not wait for the push, REST API; each guard checked by
  removing it (the matching tests fail).

Integrates AutoForgeAI#282

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeH26pcsqoxPb3CDs8g1VN
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.

Question: Would it be helpful to enable GitHub Repo Integration?

1 participant