09-Git-Workflow
🌿
Branch-per-run. Snapshots before changes. Push is always a deliberate user action.
1. Service
App\Services\Git\GitService wraps the git binary. All calls go through CommandExecutionService so they're logged in command_executions and produce events.
interface GitService {
public function clone(Project $p, string $repoUrl): GitResult;
public function pull(Project $p): GitResult;
public function status(Project $p): GitResult;
public function createBranch(Project $p, string $branchName, ?string $from = null): GitResult;
public function checkout(Project $p, string $ref): GitResult;
public function diff(Project $p, ?string $base = null): GitResult;
public function commit(Project $p, string $message, array $paths = []): GitResult;
public function push(Project $p, string $remote = 'origin', ?string $branch = null): GitResult; // gated
public function restoreCommit(Project $p, string $commitHash): GitResult;
public function currentBranch(Project $p): string;
public function workingTreeClean(Project $p): bool;
}2. Branch naming
agent/run-{run_id}-{short-slug} where the slug is Str::slug(run.title) truncated to 24 chars.
Examples:
agent/run-148-add-invoices-endpoint
agent/run-149-fix-auth-redirect
The default branch (main/master) is never modified by an agent.
3. Pre-run sequence
flowchart TD
A["Run start"] --> B["workingTreeClean?"]
B -- no --> X["emit error<br>fail run"]
B -- yes --> C["create pre_run snapshot<br>(commit hash on default branch)"]
C --> D["createBranch agent/run-{id}-{slug}"]
D --> E["checkout new branch"]
E --> F["emit git_branch_created"]
F --> G["Run proceeds"]4. During run
- File edits are applied via
WorkspaceService::applyChange(). They are not auto-committed unlesssafety.auto_git_commit=true.
- The agent can call
git(commit, …)tool to checkpoint logical steps. Each commit produces agit_operationsrow +command_completedevent.
- The agent can call
git(diff)at any time. Result goes to the console asgit_diff_created.
5. End of run
Three paths:
- Auto-commit OFF, final summary only: changes remain in the working tree on the run branch (or uncommitted, depending on agent behavior). User reviews the diff.
- Auto-commit ON (agent setting): a final commit
Agent run #{id}: {title}is created on the run branch with all outstanding changes.
- Manual approval → commit: user posts
POST /api/runs/{run}/approvewith{ commit: true }→ server commits.
In all cases, push is never automatic. Push requires POST /api/runs/{run}/push.
6. Push gating
safety.auto_git_pushmust betrueand the run must have an approvedrun_reviewand the API token must have abilitygit:push. All three are required.
- If any check fails: emit
command_failedwithwas_blocked=true, message"push blocked: <reason>".
7. Rollback
restoreCommit(hash)is invoked bySnapshotService::restore(snapshot_id).
- The current run branch is reset to the snapshot's commit hash; uncommitted changes are stashed into
agent/stash-run-{id}-{timestamp}for recovery.
- A
snapshot_createdevent withdescription: "restore checkpoint"is written.
8. Conflict policy
If a git pull introduces conflicts, the orchestrator:
- Aborts the pull (
git merge --abort/git rebase --abort).
- Emits an
errorevent with the conflicting files.
- Transitions the run to
waiting_for_userasking the user to resolve manually (no automatic conflict resolution by the agent).
9. Allowed Git operations matrix
| Operation | Allowed by default | Requires approval |
|---|---|---|
| status | ✅ | — |
| diff | ✅ | — |
| branch (create/checkout) | ✅ | — |
| add / commit (on run branch) | ✅ | — |
| pull (default branch) | ✅ | — |
| restore (snapshot) | ✅ | — |
| push | ❌ | review + token ability |
| force push | ❌ | hard-blocked |
| tag push | ❌ | hard-blocked |
| branch delete (remote) | ❌ | hard-blocked |
10. Git workflow diagram
gitGraph
commit id: "main"
branch agent/run-148
commit id: "pre_run snapshot"
commit id: "feat: invoices controller"
commit id: "feat: invoice migration"
commit id: "test: invoices feature test"
checkout main
merge agent/run-148 tag: "v1.4.0 (user-approved)"11. Repository URL validation
https://andgit@schemes allowed.
- Hostname allowlist configurable (
git.allowed_hosts, defaultgithub.com,gitlab.com,bitbucket.org).
- No
file://, no relative paths.
- No credentials in URLs (
https://user:pass@…is rejected; use a GitHub App / deploy key configured viaproject_connections).