[BUG] Cloud Routines: container starts detached-HEAD over stale baked-in branch refs — `git checkout main` time-travels days back; no way to pin the checkout ref

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 17, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Summary

Cloud Routines (claude.ai/code/routines) provision the repo into the container as a detached-HEAD checkout of the current tip, laid over a cached clone whose local branch refs are frozen at environment-build time. Any agent that runs git checkout main — a completely reasonable move, especially when the routine prompt says "you are in a clone, branch main" — silently time-travels its working tree onto a days-old main ref. Agents that instead commit on the detached HEAD produce orphan chains that never reach the remote.

There is no way to pin or guarantee the checked-out ref on a routine: job_config.ccr.session_context.sources[].git_repository accepts only url. For long-running scheduled jobs that maintain state in a repo, a guaranteed start ref seems like table stakes.

Forensics (5 occurrences over 2 days, hourly routine against one private repo)

Evidence that the working-tree content is fetched fresh per run (i.e. this is not a stale snapshot of content):

  • One container started detached at a commit that had been pushed to origin/main 8 minutes before the run fired.
  • Across all 5 occurrences the detached tip advanced with the repo.

Evidence that the local branch refs are frozen at environment build:

  • One container's local main pointed at a commit ~6 days old (approximately when the environment was configured), genuinely diverged from origin/main (not an ancestor); git checkout main moved the working tree onto that stale content until the agent caught it via merge-base --is-ancestor.

Impact (real, not hypothetical)

  1. One run committed ~50 hourly cycles of append-only journal state on the detached orphan chain, unpushed; a later run force-pushed origin/main to a detached tip while trying to self-rescue, temporarily replacing 11 days of granular commit history. A post-incident audit recovered everything except one cycle's worth of journal records, but only because agents had proactively pushed backup branches.
  2. Runs that detected the condition correctly suspended their side-effecting actions for ~2 days as self-protection, because journal state on main could not be trusted and a concurrent second runner could not be ruled out.

Workaround

Every routine prompt now begins with git fetch origin && git checkout -B main origin/main plus a "never force-push" instruction. It works, but it lives in N prompts instead of one platform guarantee, and every agent has to be told not to trust the harness-provided checkout.

Ask

Either (or both):

  • At session start, sync local branch refs to the remote and attach HEAD to the default branch, so the checkout matches what the UX implies ("a clone of your repo").
  • Expose a ref/branch field on sources[].git_repository so a routine can pin "always latest origin/main" (or a fixed tag) explicitly.

Related but distinct: #69783 (harness overrides the agent's branch on the push side; this report is about the start state).

Environment

  • Product: Claude Code Routines (scheduled cloud agents, claude.ai/code/routines), Anthropic-hosted cloud environment
  • Occurrences: 2026-07-16 (×2), 2026-07-17 (×3), hourly cron

View original on GitHub ↗