◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Cowork
◈ Cowork/2026-06-30Advanced

Trusting a Three-Day-Old Mirror: Stopping Unattended Tasks from Acting on a Stale Working Copy

A persistent clone reused for speed quietly drifts from the remote. Read 'has this article been fixed yet?' from an out-of-sync tree and your unattended task duplicates work or 'succeeds' against an old world. Here is a HEAD-match plus writability preflight, with a self-healing re-clone, in bash.

Cowork46scheduled-tasks5automation109git17reliability18

✦ Premium Article

An unattended scheduled task can log "success" while the thing it produced is still from a previous timeline. As an indie developer, I run four AI blogs (Dolice Labs) as Cowork scheduled tasks, and one morning's log gave me a jolt. Before editing, I checked "has this article already been revised?" against my local clone — but that clone was three days old, and in reality another run had finished updating the very same article upstream. I was one step away from rewriting the same piece twice.

Not a single error fired. The git clone had succeeded, the files were readable, the quality gates passed. And yet the ground my decision stood on had shifted. This class of incident shows up not as a "failure" but as "correct processing on top of an outdated truth," so no exception handler will catch it. Here is how to treat a persistent clone as a cache and verify its freshness as a contract before you make any decision.

Why a persistent clone drifts in silence

For speed, most automations don't re-clone the working repo every run. They keep it somewhere like /tmp/repos/{site} and update with git pull --rebase. That's a sound optimization. The danger is that downstream steps implicitly assume the persistent clone is "the latest." Drift enters through three paths.

First, another run has already moved ahead. If several scheduled slots (morning generation, midday revision, nightly recovery) touch the same repo, another run has pushed since your last pull. Your tree doesn't know about that push.

Second, ownership flipped and the pull itself failed silently. On Cowork's VM, /tmp/repos/ can persist across sessions owned by nobody:nogroup, and then git pull and writes are both rejected. If your recovery appends || true, the step keeps going after failing — and you decide on top of an old tree.

Third, a rebase finished halfway. If a prior run hit a conflict or interruption and couldn't complete the rebase, it may leave a detached state or a partial commit. git log looks plausible but isn't current.

The common thread: readable but old. That is exactly why "could I read it?" is the wrong question, and "does it match the latest?" is the right one.

Why deciding on a stale tree breaks quietly

An unattended task reads its local tree mostly for decisions that come before processing. Questions like these:

The decisionWhat a stale tree does
Does this slug already exist?Mistakes an upstream-added article for "missing" and creates a duplicate slug
Has this article been revised already?Sees "not done" for something already fixed, and rewrites it twice
Do JA and EN counts match?Calls it balanced on an old count, missing a real mismatch
Did we just publish this topic?Reads an old article list, judges "new," and duplicates the subject

When the decision is wrong, no amount of correct processing afterward saves the result. Worse, the failure mode is "clean exit," so the log reads Status: SUCCESS. The principle I hold onto: a decision made on an unverified tree is invalid, even if its conclusion looks right. Guarantee freshness before the decision. Design it as a matter of ordering.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Three ways a persistent clone goes stale (another run advanced it after push, ownership flipped to nobody, a half-finished rebase) and how each one poisons decision reads
✦A sub-second preflight that matches local HEAD against remote via git ls-remote and probes .git writability before any decision is made
✦A self-heal flow that catches up with pull --rebase, falls back to a fresh clone under $HOME when it can't, and treats any decision made on an unverified tree as invalid
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ Cowork2026-08-23
Not Every Unreachable Connector Means the Same Thing in an Unattended Run
When an unattended task cannot reach a connector, the cause splits into three states: still connecting, unauthenticated, or genuinely absent. Each demands the opposite response. Here is the resolver that tells them apart.
◈ Cowork2026-09-16
The scheduled task that only ran on days my desk was awake
Cowork scheduled tasks run remotely by default, but the moment one needs a local file or app, it runs only on your machine. Here is how the last field in the setup dialog decides that, and the three questions I now answer before creating a task.
◈ Cowork2026-10-15
Visible in the Listing, Zero Hits by Name: Counting Japanese Filenames With Split Dakuten
In a folder I gave Cowork, Japanese filenames were missing from name searches. Counting about 19,000 entries turned up 17 names with decomposed dakuten. Here is how I counted and fixed it.