The implementer agent no longer pushes to git — the controller worker
now owns the push: it gates the agent's commits on lint+typecheck and
pushes via a single leased primitive. Closes two production defects:
- Clobber: the pre-fix MCP --force-with-lease leased against a
freshly-fetched tip, so the lease always passed — an in-flight
implementer destroyed a commit pushed to the PR branch during its
run (lost a hand-pushed skip_coverage fix on PR #46).
- Gate-skip: the agent verified only the CI-flagged gate, so a fix for
one gate shipped fresh violations in another (lint flapped
pass->fail across CI runs 198->199).
Step 1 — worker_push primitive:
- New git_push.py: one leased push, pinned to the SHA the worker
started from; classifies pushed / stale_input / diverged /
infra_error; bounded infra-retry.
- mcp_git_server.push gains expected_sha for a correctly-pinned lease.
- finalize_conflict_resolution migrated onto worker_push.
Step 2 — deterministic gate:
- New gate.py: per-slot nox env-dirs (no venv races), manifest-hash
staleness keying, lazy warm-up.
- local_ci_gate.sh gains --envdir.
Step 3 — worker-owned gated push:
- New implementer_finalize.py: divergence pre-check -> lint+typecheck
gate -> ruff auto-fix -> leased push. finalize's outcome is
authoritative over the agent's emitted outcome.
- agent_runner integrates finalize; salvage no longer pushes.
- outcomes/tick: gate-failed + push-time stale-input caps,
epoch-scoped; WorkerError carries an output_payload so the gate
report reaches the next attempt's prompt; prefetch surfaces
gate-failed attempts.
- The 5 task-implementor prompts drop the agent push step.
Reviewed across 4 adversarial rounds; full controller+MCP suite green
(1379 passed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR branches 177-180 commits ahead of base cannot be rebased
commit-by-commit by a single-shot resolver agent (too many conflict
stops for one session). Conflict-prep now defaults to rebase (linear
history) and falls back to a single 3-way merge when the branch is too
divergent (commit count over CONTROLLER_CONFLICT_REBASE_MAX_COMMITS,
default 60). The merge pipeline derives the track from branch shape via
a Do:rebase -> Do:merge ladder in _make_merge_pr — no stored flag.
Adds a git_rebase_continue MCP tool plus status rebase/merge-in-progress
fields so the conflict-resolver agent is fully MCP-driven and dual-mode
(mid-rebase or mid-merge). Also routes a green-CI implementer noop
straight to REVIEWING instead of a deadlock-prone AWAITING_CI round
trip. No state-machine change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Run-2 PR #40 exposed that the conflict-resolver stage never resolved
conflicts *on the PR*. The resolver agent owned the whole git flow
(rebase + resolve + push) but was never given the PR head branch name,
so it pushed the resolved branch to a guessed branch ("main"), the push
was lost, and it reported "resolved" anyway — the controller advanced
the workflow on a resolution that never reached the PR.
This splits the work along the controller's principle: deterministic
git mechanics in the worker, only the semantic merge in the agent.
- conflict_rebase.py (new): the worker starts the rebase
(prepare_conflict_rebase — hands the agent a real mid-rebase
worktree) and, after the agent's resolution, lands it with a
*verified* push (finalize_conflict_resolution) — force-with-lease
pinned to the PR head the resolver started from, then re-reads the
remote to confirm it moved. A failed/rejected push is never reported
as resolved. run_conflict_resolver_attempt orchestrates it.
- ConflictResolverInputV1 / prefetch: carry head_ref so the worker
knows the PR branch (this also makes the worker's fetch_and_validate
run for the conflict_resolver — it gated on head_sha AND head_ref).
- worker/__main__.py: route conflict_resolver attempts through the new
orchestration.
- conflict-resolver-worker.md: the agent resolves + continues the
rebase and never pushes — the controller lands it.
- mcp_git_server.py: add the controller worktree root to the git MCP's
allowed bases (every git_* MCP call errored for this role before).
The committed-marker check is scoped to the PR's own diff — a
whole-tree grep false-positived on repo files that legitimately
document conflict markers. The worker's rebase/push are lost-lock
guarded so a reaped worker cannot race the shared worktree.
1136 controller tests pass (+11 new in test_conflict_rebase.py).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Applies `ruff format` to the accumulated formatting debt on this branch.
Formatting-only — no behavioral changes. Required for CI/lint's format
gate (`nox -s format -- --check`), which the branch was failing on 288
tracked files that drifted from ruff's canonical style.
In-progress WIP files are intentionally excluded so this commit stays a
clean formatting-only diff.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>