Skip to content

afx spawn cuts worktrees from the shared checkout's stale local HEAD with no fetch, no staleness guard, and no base report #1716

Description

@amrmelsayed

Field incident

Four lanes on one adopter workspace lost real work in a single day to spawns cut from a stale base. afx spawn branches the builder worktree from the shared checkout's LOCAL HEAD (verified: spawn-worktree.ts ~213 creates the worktree from the local branch with no git fetch on the normal path; only the fork/--branch path fetches). Confirmed instances, independently: a worktree cut 28 commits behind origin (the architect had verified the defect ON origin's branch and run a full pre-spawn checklist), 59 behind, and 141 behind (~20 hours; caught in plan phase after a 38-file inventory had to be re-verified against the merged tree). Two lanes were clean. Spawn reported [ok] in all cases.

Why a checklist is not the fix

The originating architect's checklist PASSED WHILE BEING WRONG: they asserted against origin/<branch> while the action consumed local HEAD. And "assert local==origin before every spawn" is a vigilance control on a shared resource with no signal — whoever fetches benefits everyone silently, whoever leaves the checkout stale poisons everyone silently, and nobody is told either way.

Design tension to respect (this repo's own workflow)

Branching from local HEAD is partly deliberate: approved specs/plans must be COMMITTED before spawning precisely so the builder (which branches from HEAD) can see them, including commits not yet pushed. So "always branch from origin" would break the commit-locally-then-spawn flow. The poison case is specifically the local base being BEHIND origin; being AHEAD (unpushed local commits) is legitimate.

Two asks (the second is cheaper and valuable even after the first lands)

  1. Guard on staleness: before cutting the worktree, git fetch the base branch's remote ref and compute behind-count (git rev-list --count <base>..origin/<base>). Behind > 0 → refuse with the count and the remedy (or --force past it, mirroring the dirty-worktree guard — same guard class, same place). Ahead-only → proceed (legitimate unpushed work). Offline/fetch-failure → proceed with a loud UNKNOWN-staleness warning, never a silent pass (do not render unknown as determinate).
  2. Report the base regardless: replace bare [ok] spawned with branched from <sha> (<base>, N behind / M ahead of origin/<base>). Even a correct base is something an architect wants to see; staleness becomes visible at spawn time with nobody remembering to run rev-list.

Audit instruments (from the reporting workspace, for docs/tests)

  • Post-hoc check for an existing worktree: git rev-list --count <builderHEAD>..origin/<base>.
  • Spawn-time staleness forensics: git reflog show origin/<base> --date=iso — commit dates lie (a branch authored days earlier and merged later carries old committer dates).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/towerArea: Tower server / agent farm CLI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions