Skip to content

ensure_branch: the three arms that cut a branch from an unfetched cache report it to the caller #245

Description

@blooop

Work

ensure_branch (devlaunch/worktree/workspace_clone.py:504-569) has three arms that
cut a new branch from the bare cache without having refreshed the ref it bases on, each
reporting only a logger.warning:

  1. FetchFailed on the default-branch fetch (:544-551) — the remote was reachable
    enough to answer RefMissingOnRemote for the ticket branch, but the follow-up fetch
    of the default branch failed. The branch is then cut from whatever the cache holds.
  2. get_default_branch raises (:507-510) — default_branch becomes None, so the
    RefMissingOnRemote arm fetches nothing, and ensure_branch_exists is called with
    start_point="HEAD" (:567) — the bare cache's own HEAD, of unbounded age.
  3. ValueError on the recorded default-branch name (:528-532) — same shape as (2):
    no fetch, warning only, branch cut from cache.

In all three the launch reports success and the agent starts work on a tree that may be
arbitrarily far behind main. Observed in the wild on blooop/bencher: a dl workspace
checked out 201 commits behind origin/main with no error, which then reproduced a
pyproject.toml deprecation warning upstream had already fixed.

Also settle the start point in (2)/(3): create_local_branch runs
git branch <branch> <start_point> (branch_manager.py:78) against the bare's local
head name, which is only equal to origin's tip because the force refspec
+refs/heads/<b>:refs/heads/<b> just wrote it. When no fetch ran, that equality is
unproven — "HEAD" especially so.

Not in scope: refusing to launch, and any broad +refs/heads/* fetch on the
foreground path. #144's contract stands
test_an_unreachable_remote_launches_from_the_cache must keep passing. The goal is
that a caller (and wf, which reads dl's output) can tell a fresh base from a stale
one, not that stale bases become fatal.

Also not in scope, and worth its own ticket: relaunching an existing workspace clone
never advances it — _prepare_workspace issues a plain git checkout <branch>
(workspace_clone.py:711-713) and the ref ensure_branch just fetched into .bare goes
unread; the devpod fast-attach at dl.py:4650-4655 skips prepare_cold altogether. Both
are pinned as intended behavior today (test_existing_workspace_uses_plain_checkout,
test/test_workspace_clone.py:982), and changing them means deciding what happens to
local work — a decision, not this ticket.

Done when

  • Each of the three arms above is covered by a failing-first test that asserts the new
    branch's base, and the arm reports its degraded state through a channel a caller can
    read — not only a log line.
  • test_a_brand_new_branch_starts_from_the_current_default_branch and the whole of
    test/test_cold_launch_fetches.py still pass unchanged.
  • Fetch count on the happy path is unchanged (test_the_whole_launch_costs_one_fetch,
    test_absent_ref_costs_exactly_one_extra_fetch).

Files: devlaunch/worktree/workspace_clone.py, devlaunch/worktree/branch_manager.py,
test/test_workspace_clone.py (TestEnsureBranch, :217),
test/integration/test_repo_manager_real.py (TestStalenessContract, :267).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions