Skip to content

fix(bin): let a spawn proceed on a project with no origin remote - #2874

Open
matixacik-cell wants to merge 2 commits into
kunchenguid:mainfrom
matixacik-cell:fm/spawn-no-remote-fix
Open

fix(bin): let a spawn proceed on a project with no origin remote#2874
matixacik-cell wants to merge 2 commits into
kunchenguid:mainfrom
matixacik-cell:fm/spawn-no-remote-fix

Conversation

@matixacik-cell

Copy link
Copy Markdown

The defect

bin/fm-spawn.sh's freshen_spawn_worktree_base() unconditionally ran git -C "$worktree" fetch --quiet origin and returned a hard error when that failed.

A project with no origin remote configured, such as a purely local checkout that has never been pushed anywhere, therefore could not be spawned into a fresh pooled worktree at all. The fetch cannot succeed because there is no remote to fetch from, so every such spawn was refused with:

error: could not fetch origin for pooled worktree '<path>'; refusing to launch from a potentially stale base

Exact reproduction

Against the current default branch (f170ced), with a repository that has no origin remote:

$ git init -b main localonly && cd localonly
$ git commit -m initial          # any commit
$ git worktree add --detach ../pool HEAD
$ git -C ../pool remote          # prints nothing: no remote configured
$ freshen_spawn_worktree_base ../pool
fatal: 'origin' does not appear to be a git repository
fatal: Could not read from remote repository.
error: could not fetch origin for pooled worktree '.../pool'; refusing to launch from a potentially stale base

Work already running in previously initialised worktrees was unaffected, which is why this only surfaced once a fresh pool slot was needed.

The fix

Freshening asks one question: is this worktree's base stale against its upstream? A repository with no origin configured has no upstream, so that question is meaningless rather than failed.

freshen_spawn_worktree_base() now reads git's own local remote list first, which makes no network call, and skips the refresh with a loud notice when origin is not configured:

notice: pooled worktree '<path>' has no origin remote configured; skipping the base refresh because there is no upstream it can be stale against

The distinction is drawn on whether a remote is configured, never on whether a command failed. Every existing protection is unchanged for a repository that does have an origin: an origin that cannot be reached, a default branch that cannot be resolved, a dirty worktree, a failed reset, and a post-reset SHA mismatch all still refuse exactly as before. A network outage on an ordinary project is never reclassified as "no remote". A failure to read the remote list itself is also treated as a refusal, so the unsafe direction stays closed.

Skipping the refresh does not skip the unlanded-work protection. A pooled worktree carrying uncommitted work is still refused on the no-origin path, because a new task must never silently start on top of another task's unlanded work, and that risk is identical with or without an upstream. That path gets its own accurate message, since it is refusing to launch onto unlanded work rather than refusing to discard while refreshing:

error: pooled worktree '<path>' has uncommitted work; refusing to start a new task on top of it

The origin path's existing wording is untouched.

Placement

The check lives inside freshen_spawn_worktree_base() rather than at its call site, so the freshening contract, including when it does not apply, stays with its one owner and no caller has to know what makes a base refresh meaningful.

Tests

Colocated in the existing tests/fm-spawn-pool-base-freshen.test.sh, covering both directions plus the retained protection:

  • a repository with no origin spawns and prints the skip notice
  • a configured but unreachable origin still refuses, and explicitly must not be reported as a missing remote
  • a dirty worktree with no origin still refuses, does not announce a skipped refresh, does not move HEAD, and preserves the uncommitted file

The no-origin test was proven to fail on the unfixed script (expected exit 0, got 1) and pass with the fix.

Scope

validate_spawn_worktree()'s isolation assertion is untouched, and nothing under projects/ is touched. bin/fm-spawn.sh's header and one paragraph in docs/architecture.md are updated because the fix makes their previous wording inaccurate.

fm-spawn's pooled-worktree base refresh unconditionally fetched origin and
refused the spawn when that failed, so every fresh task worktree on a project
that has no origin remote at all was refused with "could not fetch origin".

Freshening asks whether a worktree's base is stale against its upstream. A
repository with no origin configured has no upstream, so that question is
meaningless rather than failed. Read git's local remote list first, which makes
no network call, and skip the refresh with a loud notice when origin is not
configured. A configured origin that cannot be reached, an unresolved default
branch, a dirty worktree, and a stale base all still refuse exactly as before,
so a network outage is never reclassified as "no remote".

The check lives inside freshen_spawn_worktree_base so the freshening contract,
including when it does not apply, stays with its one owner.
@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Confidence Score: 4/5

The PR should not merge until the no-origin path prevents a pooled worktree containing a previous scout's committed work from becoming the next task's base.

Scout teardown can return a detached worktree at an unlanded commit, while the new no-origin path checks only uncommitted status and then launches without restoring a known clean base.

Files Needing Attention: bin/fm-spawn.sh

Reviews (1): Last reviewed commit: "no-mistakes(review): refuse dirty pooled..." | Re-trigger Greptile

Comment thread bin/fm-spawn.sh
Comment on lines +1739 to +1740
status=$(git -C "$1" status --porcelain) || return 2
[ -z "$status" ]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Committed scout state passes clean check

When a scout commits changes before its scratch worktree is returned to a no-origin pool, git status --porcelain reports the detached worktree as clean and this path launches without resetting HEAD, causing the next task to branch on top of the previous scout's unlanded commit.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants