# ADR-0016: Rebase the Job branch onto its base at the start of every Run

- **Status:** Accepted
- **Date:** 2026-09-09

## Context

A Job can span several nights ([ADR-0011](https://codingowl.dev/docs/decisions/0011-idle-policy-and-interruption)), and its base branch moves while it
does. That undermines two things at once. Verification ([ADR-0013](https://codingowl.dev/docs/decisions/0013-verification-gates-completion)) run against a
week-old base can fail for reasons that have nothing to do with the Job, and
pass for reasons that will not survive contact with current code. And the diff
presented for review is against a base nobody is on any more.

## Decision

At the start of **every** Run, Owl fetches and rebases the Job's branch onto the
Project's base branch.

The fetch updates the remote-tracking ref of the base branch,
`refs/remotes/<remote>/<base>`, and nothing else. The rebase then targets:

- `refs/remotes/<remote>/<base>` when the fetch succeeded, so that the branch
  is replayed onto what the remote knows rather than onto what the user last
  pulled;
- `refs/heads/<base>`, the local base branch, when the Project has no remote
  or the fetch failed.

The local base branch is never moved by Owl: it is the user's, and a fetch
that reaches the remote makes the rebase independent of it.

- Clean rebase - the Run proceeds, and Verification runs against the rebased
  state.
- Conflict - `git rebase --abort`, and the Job moves to `blocked` naming the
  conflicting paths and the ref the rebase was onto.

The first Run of a Job has nothing to rebase; its branch is cut fresh from the
base.

## Consequences

- A green Job is green against today's base, which is the only claim worth
  making.
- The review diff is linear and against current `main`.
- Rebasing rewrites the commits the Agent made. Since [ADR-0026](https://codingowl.dev/docs/decisions/0026-phases-and-handoff-continuity) gives every Run a
  fresh context, nothing carries a memory of the old shas; the next Run reads the
  handoff document and the rebased log as it finds them. A handoff that names a
  sha will not resolve after a rebase, so it should describe work in prose.
- The daemon rewrites git history unattended. That is bounded by the fact that
  the branch is Owl's own, exists only for this Job, and is never pushed
  ([ADR-0015](https://codingowl.dev/docs/decisions/0015-job-disposal-and-garbage-collection)). Owl must never rebase anything else.
- Abort handling has to be solid: a Run must never begin on top of a worktree
  left mid-rebase. Garbage collection ([ADR-0015](https://codingowl.dev/docs/decisions/0015-job-disposal-and-garbage-collection)) should detect and report that
  state rather than trying to resolve it.
- "Conflict blocks the Job" means an active Project can block a long-running Job
  repeatedly. That is the honest signal, and the fix is smaller Jobs.

## Alternatives considered

**Merge the base in instead of rebasing.** Does not rewrite the Agent's commits,
which mattered while Sessions were resumed across nights. [ADR-0026](https://codingowl.dev/docs/decisions/0026-phases-and-handoff-continuity) removed that
concern entirely, leaving only the objection that merge commits clutter a
short-lived branch that exists to be reviewed once.

**Leave the branch alone.** Perfectly predictable, and no unattended git
operation can surprise anyone. Rejected because it makes Verification a claim
about a base that no longer exists, which is the one thing the gate in [ADR-0013](https://codingowl.dev/docs/decisions/0013-verification-gates-completion)
was added to prevent.

---

Source: [docs/adr/0016-rebase-job-branch-each-run.md](https://github.com/vojtechmares/coding-owl/blob/main/docs/adr/0016-rebase-job-branch-each-run.md)
