Skip to content

fix: support remote-less local-only task lifecycles - #3255

Closed
SamSherpaDev wants to merge 12 commits into
kunchenguid:mainfrom
SamSherpaDev:fm/fm-spawn-remoteless
Closed

SamSherpaDev wants to merge 12 commits into
kunchenguid:mainfrom
SamSherpaDev:fm/fm-spawn-remoteless

Conversation

@SamSherpaDev

Copy link
Copy Markdown

Intent

Fix Firstmate's fresh spawn path so a genuinely remote-less local project can dispatch scouts and local-branch ship work without weakening stale-base protection for remote-backed projects. Reproduce the defect through the executable spawn interface using a temporary Git repository with an initial commit and no origin. A registered local-only project must launch both scouts and ships whose concrete delivery mode is local-only without inventing or requiring origin, and remote-less local-only scouts and promotions may proceed, while no-mistakes, direct-PR, and no-mistakes-prod-only project postures, PR-backed spawn contracts, and PR-backed promotion must require a valid origin; a missing, unreadable, or failing configured origin must never fall back to the supported remote-less path. Prove both the authoritative project and task worktree have no configured remotes before local fallback. Resolve local default branches without stale remote-tracking refs and keep that resolution consistent across spawn, promotion, review, local landing, and cleanup. Preserve worktree isolation, pooled-copy freshness, all supported backend paths, task identity, and concurrent spawn guarantees, explicitly treating non-applicable axes as such. Keep ownership in the existing spawn/worktree and shared project-mode/default-branch contracts without a second parser or broad fallback. Add focused executable regression coverage for remote-less scout and local-only ship and promotion success plus remote-backed missing and failing-origin refusal, never tests that assert implementation-source bytes. Update only concise authoritative help or maintainer docs that would otherwise be inaccurate. Verify focused and changed-area tests, bin/fm-lint.sh, and bin/fm-doc-audience-check.sh. Keep documentation and the PR description short and bulleted around important behavior, risks, and verification; add no agent co-author; run no live Herdr lifecycle tests from this unguarded task. Produce a current-head green PR and do not merge it.

What Changed

  • Allow registered local-only projects to spawn scouts and local-only ships, promote tasks, review, land, and clean up using a local default branch when both project and task worktrees have no remotes.
  • Centralize lifecycle base resolution while preserving origin freshness requirements for remote-backed and PR-backed workflows and rejecting stale remote-tracking evidence during cleanup.
  • Add executable regression coverage for remote-less success paths and missing, failing, or invalid origin refusals, with concise architecture and script documentation updates.

Risk Assessment

✅ Low: The shared resolver and cleanup safeguards are fail-closed, consistently applied across lifecycle paths, and supported by focused executable regression coverage.

Testing

The target started and ended clean. Focused executable and lifecycle tests validated remote-less scout, local-only ship and promotion success; missing, unresolved, stale, and failing-origin refusal; pooled-base freshness; review, local landing, cleanup, tangle, and secondmate-sync behavior. The captured CLI transcript directly shows both repositories had no remotes before fallback and the resulting persisted contracts. Backend-specific launch mechanics and concurrent-spawn axes were non-applicable because the changed resolver is in the shared pre-launch path; no live Herdr lifecycle was run. Lint and documentation-audience checks remain with their outer gate phases.

Evidence: Executable remote-less spawn, promotion, and origin-refusal transcript

Source: Executable remote-less spawn, promotion, and origin-refusal transcript

=== Remote-less fixture before fallback ===
authoritative project remotes: <none>
task worktree remotes: <none>
stale task HEAD: c3ea7ca8959d70cb335e483e6c1251686d2c7362
current local main: 005339d157737fa8284afbe715e19ad9424598ee
=== Scout spawn ===
spawned evidence-scout-r1 harness=codex kind=scout window=firstmate:fm-evidence-scout-r1 worktree=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/task-worktree
task HEAD after scout: 005339d157737fa8284afbe715e19ad9424598ee
local-main content in task: current local default
scout persisted contract:
worktree=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/task-worktree
project=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T/fm-remoteless-evidence.nry2pv/project
kind=scout
=== Local-only ship spawn ===
spawned evidence-ship-r1 harness=codex kind=ship mode=local-only yolo=off window=firstmate:fm-evidence-ship-r1 worktree=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/task-worktree
task HEAD after ship: 005339d157737fa8284afbe715e19ad9424598ee
ship persisted contract:
worktree=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/task-worktree
project=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T/fm-remoteless-evidence.nry2pv/project
kind=ship
mode=local-only
yolo=off
=== Remote-less local-only promotion ===
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  3 task(s) in flight, but no live watcher process holds this home lock (last beat: 6s ago).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the guarded operation WILL still run.
●  repair a missing or failed watcher cycle with the Pi tool fm_watch_arm_pi, or restart Pi with -e /Users/trpmac2/.no-mistakes/worktrees/354ca8d119eb/01M14TFTPT2NQDQRHKVHFG7RB7/.pi/extensions/fm-primary-turnend-guard.ts -e /Users/trpmac2/.no-mistakes/worktrees/354ca8d119eb/01M14TFTPT2NQDQRHKVHFG7RB7/.pi/extensions/fm-primary-pi-watch.ts if the extensions are not loaded.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
promoted evidence-promote-r1 to ship mode=local-only yolo=off (teardown protection restored)
next: FM_HOME=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/home bin/fm-send.sh fm-evidence-promote-r1 '<ship instructions for mode=local-only: review scratch state with git status and git log; reset to a clean default-branch base; carry over only intended fix changes; create branch fm/evidence-promote-r1; implement; report done>'
promotion persisted contract:
worktree=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/task-worktree
project=/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/project
kind=ship
mode=local-only
yolo=off
=== PR-backed spawn refusal with no origin ===
exit=1
error: task worktree '/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/task-worktree' has no origin remote; this lifecycle requires a valid origin; refusing to launch from a potentially stale base
=== Configured failing-origin scout refusal (no local fallback) ===
configured remotes:
origin	file:///var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/missing-origin.git (fetch)
origin	file:///var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/missing-origin.git (push)
exit=1
error: could not fetch origin for task worktree '/var/folders/r4/lj5nyr393lv0s3j7ltth6dxc0000gn/T//fm-remoteless-evidence.nry2pv/task-worktree'; refusing to launch from a potentially stale base

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (7) ✅
  • 🚨 bin/fm-ff-lib.sh:37 - Intent requires “keep that resolution consistent across spawn, promotion, review, local landing, and cleanup,” but this stale-ref-safe local resolver is used only by spawn. In a remote-less repo with current main, old trunk, and stale origin/HEAD -&gt; origin/trunk, spawn correctly bases work on main; fm-review-diff.sh reviews against trunk, fm-merge-local.sh refuses because the project is on main, and fm-teardown.sh treats work landed on main as unmerged. Put remote-aware default resolution at the shared lifecycle boundary and use it across these paths.
  • 🚨 bin/fm-promote.sh:116 - The promotion guard does not fully enforce the required origin policy. local-only bypasses all checks, so a configured origin that became unreachable can still be treated as the local path without proving both project and task worktree have no remotes; PR modes accept any successful fetch, so an origin whose HEAD names a missing branch is promoted even though spawn rejects that same unresolved default. This contradicts “PR-backed promotion must require a valid origin” and “Prove both the authoritative project and task worktree have no configured remotes before local fallback.” Reuse the shared remote/default validation before mutating metadata.

🔧 Fix: Unify lifecycle base resolution and promotion origin validation
3 errors still open:

  • 🚨 bin/fm-promote.sh:131 - Intent requires “no-mistakes, direct-PR, and no-mistakes-prod-only project postures ... require a valid origin” and forbids a missing origin from falling back. This enables remote-less promotion solely from MODE=local-only, without checking registered posture. A no-mistakes scout can spawn with origin, lose origin, then promote local-only successfully; merge and teardown likewise pass unconditional remote-less eligibility. Gate fallback through the existing project-posture contract before metadata changes.
  • 🚨 bin/fm-review-diff.sh:62 - Intent requires consistent resolution across spawn and review, while only PR-backed task contracts require task-worktree origin. A scout has no mode, so this always requires task origin. With worktree-specific config where the authoritative project has a valid origin but the task worktree has no remotes, spawn correctly refreshes the scout through the project origin, but review refuses it. Require task origin only for PR-backed ship modes.
  • 🚨 bin/fm-teardown.sh:1388 - The new local-base safety check is reached only when git log HEAD --not --remotes reports commits. In a genuinely remote-less repository, a stale refs/remotes/origin/fm/&lt;id&gt; at task HEAD makes that result empty even when local main lacks the commit, so teardown skips this check and deletes the unlanded branch and worktree. For a resolved remote-less local-only task, always verify containment in the local default before trusting remote-tracking refs.

🔧 Fix: Gate remote-less lifecycle and protect cleanup
2 issues (1 error, 1 warning) still open:

  • 🚨 bin/fm-promote.sh:120 - The new unconditional worktree/project validation makes three existing executable cases in tests/fm-public-followup.test.sh (lines 2164, 2178, and 2194) exit before their expected successful promotion and parent-binding assertions, because those scout metas contain only window/kind. The current source therefore cannot be green. Update those fixtures with a real eligible local-only project, worktree, registry, and metadata; do not weaken this validation.
  • ⚠️ bin/fm-teardown.sh:1388 - Local-only teardown now resolves and validates origin even when git log HEAD --not --remotes is already empty. A remote-less task subsequently pushed to a fork remote with no origin is safely preserved on that remote, but teardown now refuses with “configured remotes but no origin,” regressing the established any-remote cleanup contract. Preserve remote containment before resolving a local default; still force local-default containment when both repositories truly have no remotes so stale tracking refs remain untrusted.

🔧 Fix: Fix promotion fixtures and preserve fork-backed cleanup
1 error still open:

  • 🚨 bin/fm-review-diff.sh:61 - Intent requires consistent origin handling across spawn and review, but task-origin enforcement now applies only when mode is explicitly no-mistakes or direct-PR. A supported legacy ship with no mode= (elsewhere intentionally defaulted to no-mistakes), a project-only origin, pr=, and a stale reachable pr_head= passes base resolution; pull-head fetch then fails and review silently shows the stale recorded SHA. Default absent non-scout modes to no-mistakes, exempting only scouts and explicit local-only tasks.

🔧 Fix: Default legacy reviews to PR-backed origin validation
2 errors still open:

  • 🚨 bin/fm-merge-local.sh:58 - Intent requires default resolution to stay consistent through local landing, but the freshly resolved FM_PROJECT_BASE_COMMIT is ignored. If a remote-backed local-only task branches from A, origin advances to B, and local main remains A, this checks only A→task and merges successfully without B. Require the resolved base commit to be an ancestor of the task branch before landing.
  • 🚨 bin/fm-teardown.sh:1402 - Stale refs can still authorize deletion when any remote is configured. With unlanded HEAD recorded by stale refs/remotes/origin/fm/&lt;id&gt;, then origin removed and an unrelated fork remote added without pushing, --not --remotes is empty and remote presence suppresses the local-default check, so teardown deletes the work. Prove containment through a currently configured remote before skipping the local check.

🔧 Fix: Enforce fresh-base landing and configured-remote cleanup safety
1 error still open:

  • 🚨 bin/fm-teardown.sh:1039 - Configured-remote containment still trusts local tracking refs without verifying them remotely. If origin remains configured but origin/fm/&lt;id&gt; is stale after the remote branch was deleted, force-pushed, or the origin URL was replaced, this helper excludes the unlanded HEAD, unpushed becomes empty, and teardown deletes the branch/worktree without any landing check. Build exclusions only from refs currently advertised by each configured remote, failing closed when containment cannot be verified.

🔧 Fix: Verify remote containment before teardown cleanup
1 warning still open:

  • ⚠️ bin/fm-teardown.sh:1043 - Remote containment is ignored when an advertised branch tip is not already in the local object store. If task commit H was pushed to a fork and another clone advances that branch to descendant D, ls-remote advertises D, this line skips it, and teardown falsely reports H as unpushed despite its remote containment. Fetch advertised heads into temporary refs before computing reachability.

🔧 Fix: Fetch advertised remote descendants before teardown containment checks
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • git diff --stat 1fd7ea289b7a4c23a1fd9474680ed2facd6b7dd1..ad8be7d41771825692bc5e7f6bfeedc7176bb257 and changed-file inspection
  • git diff --check 1fd7ea289b7a4c23a1fd9474680ed2facd6b7dd1..HEAD
  • bin/fm-test-run.sh tests/fm-spawn-pool-base-freshen.test.sh
  • bin/fm-test-run.sh tests/fm-task-delivery.test.sh
  • bin/fm-test-run.sh tests/fm-review-diff.test.sh
  • bin/fm-test-run.sh tests/fm-teardown.test.sh
  • bin/fm-test-run.sh tests/fm-public-followup.test.sh
  • bin/fm-test-run.sh tests/fm-tangle-guard.test.sh
  • bin/fm-test-run.sh tests/fm-secondmate-sync.test.sh
  • Manual temporary-repository harness invoking bin/fm-spawn.sh for remote-less scout and local-only ship success, PR-backed and failing-origin refusal, plus bin/fm-promote.sh local-only promotion; recorded remotes, refreshed HEAD, persisted task contracts, output, and exit statuses
  • git status --short before and after testing; verified no worktree artifacts remained
✅ **Document** - passed

✅ No issues found.

🔧 **Lint** - 1 issue found → auto-fixed (2) ✅
  • ⚠️ linter found issues (exit code 1)

🔧 Fix: Suppress library output warnings and remove unused fixture variable
1 warning still open:

  • ⚠️ linter found issues (exit code 1)

🔧 Fix: Confirm full lint passes with pinned tools
✅ Re-checked - no issues remain.

✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Reviews (4): Last reviewed commit: "no-mistakes: apply CI fixes" | Re-trigger Greptile

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: first-time fork CI approved after a diff review (no .github/workflows writes). Runs 33212058529 (CI) and 33212058549 (Require no-mistakes).

Shared fm_project_base_resolve keeps origin freshness for remote-backed / PR-backed work and only permits remote-less local default after both project and task worktree prove they have no remotes and the project is registered local-only. Teardown containment now uses currently advertised remote heads. Overlaps bin/fm-teardown.sh with open #3265 and #3252; do not land leftovers together. Not merge-ready.

VISION.md per-rule:

  • One captain, one interface: aligns.
  • Authority is explicit and never inferred: aligns (local-only is an existing explicit posture; missing origin does not silently fall back).
  • Scripts own the mechanics, agents own the judgment: aligns (one resolver).
  • A restart is a non-event: aligns (fail closed rather than stale-base launch).
  • Delegation with a spine: aligns (local-only delivery already in the contract).
  • The fleet outlives any vendor: aligns (no forge required for local-only).
  • Scope: aligns.

contract-class: restore (registered local-only already promised a remote-less lifecycle; spawn currently refuses it).

@SamSherpaDev
SamSherpaDev force-pushed the fm/fm-spawn-remoteless branch from a1f040f to 856045b Compare August 30, 2026 15:55
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