fix: support remote-less local-only task lifecycles - #3255
SamSherpaDev wants to merge 12 commits into
Conversation
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Reviews (4): Last reviewed commit: "no-mistakes: apply CI fixes" | Re-trigger Greptile |
|
Speaking as Kun's firstmate: first-time fork CI approved after a diff review (no Shared VISION.md per-rule:
contract-class: restore (registered local-only already promised a remote-less lifecycle; spawn currently refuses it). |
d61ada9 to
a1f040f
Compare
…own containment checks
… fixture variable
a1f040f to
856045b
Compare
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
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
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 currentmain, oldtrunk, and staleorigin/HEAD -> origin/trunk, spawn correctly bases work onmain;fm-review-diff.shreviews againsttrunk,fm-merge-local.shrefuses because the project is onmain, andfm-teardown.shtreats work landed onmainas 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-onlybypasses 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 fromMODE=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 whengit log HEAD --not --remotesreports commits. In a genuinely remote-less repository, a stalerefs/remotes/origin/fm/<id>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 whengit log HEAD --not --remotesis already empty. A remote-less task subsequently pushed to aforkremote with nooriginis 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 explicitlyno-mistakesordirect-PR. A supported legacy ship with nomode=(elsewhere intentionally defaulted to no-mistakes), a project-only origin,pr=, and a stale reachablepr_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 resolvedFM_PROJECT_BASE_COMMITis 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 stalerefs/remotes/origin/fm/<id>, then origin removed and an unrelatedforkremote added without pushing,--not --remotesis 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. Iforiginremains configured butorigin/fm/<id>is stale after the remote branch was deleted, force-pushed, or the origin URL was replaced, this helper excludes the unlanded HEAD,unpushedbecomes 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-remoteadvertises 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..ad8be7d41771825692bc5e7f6bfeedc7176bb257and changed-file inspectiongit diff --check 1fd7ea289b7a4c23a1fd9474680ed2facd6b7dd1..HEADbin/fm-test-run.sh tests/fm-spawn-pool-base-freshen.test.shbin/fm-test-run.sh tests/fm-task-delivery.test.shbin/fm-test-run.sh tests/fm-review-diff.test.shbin/fm-test-run.sh tests/fm-teardown.test.shbin/fm-test-run.sh tests/fm-public-followup.test.shbin/fm-test-run.sh tests/fm-tangle-guard.test.shbin/fm-test-run.sh tests/fm-secondmate-sync.test.shManual temporary-repository harness invokingbin/fm-spawn.shfor remote-less scout and local-only ship success, PR-backed and failing-origin refusal, plusbin/fm-promote.shlocal-only promotion; recorded remotes, refreshed HEAD, persisted task contracts, output, and exit statusesgit status --shortbefore and after testing; verified no worktree artifacts remained✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed (2) ✅
🔧 Fix: Suppress library output warnings and remove unused fixture variable
1 warning still open:
🔧 Fix: Confirm full lint passes with pinned tools
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.