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:
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.
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.
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).
Work
ensure_branch(devlaunch/worktree/workspace_clone.py:504-569) has three arms thatcut a new branch from the bare cache without having refreshed the ref it bases on, each
reporting only a
logger.warning:FetchFailedon the default-branch fetch (:544-551) — the remote was reachableenough to answer
RefMissingOnRemotefor the ticket branch, but the follow-up fetchof the default branch failed. The branch is then cut from whatever the cache holds.
get_default_branchraises (:507-510) —default_branchbecomesNone, so theRefMissingOnRemotearm fetches nothing, andensure_branch_existsis called withstart_point="HEAD"(:567) — the bare cache's own HEAD, of unbounded age.ValueErroron 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 onblooop/bencher: adlworkspacechecked out 201 commits behind
origin/mainwith no error, which then reproduced apyproject.tomldeprecation warning upstream had already fixed.Also settle the start point in (2)/(3):
create_local_branchrunsgit branch <branch> <start_point>(branch_manager.py:78) against the bare's localhead 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 isunproven —
"HEAD"especially so.Not in scope: refusing to launch, and any broad
+refs/heads/*fetch on theforeground path. #144's contract stands
—
test_an_unreachable_remote_launches_from_the_cachemust keep passing. The goal isthat a caller (and
wf, which readsdl's output) can tell a fresh base from a staleone, not that stale bases become fatal.
Also not in scope, and worth its own ticket: relaunching an existing workspace clone
never advances it —
_prepare_workspaceissues a plaingit checkout <branch>(
workspace_clone.py:711-713) and the refensure_branchjust fetched into.baregoesunread; the devpod fast-attach at
dl.py:4650-4655skipsprepare_coldaltogether. Bothare pinned as intended behavior today (
test_existing_workspace_uses_plain_checkout,test/test_workspace_clone.py:982), and changing them means deciding what happens tolocal work — a decision, not this ticket.
Done when
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_branchand the whole oftest/test_cold_launch_fetches.pystill pass 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).