Skip to content

fix(spawn): resolve bare project names from home - #2911

Open
haroarthur wants to merge 4 commits into
kunchenguid:mainfrom
haroarthur:fm/firstmate-spawn-cd-target-bug
Open

fix(spawn): resolve bare project names from home#2911
haroarthur wants to merge 4 commits into
kunchenguid:mainfrom
haroarthur:fm/firstmate-spawn-cd-target-bug

Conversation

@haroarthur

@haroarthur haroarthur commented Aug 24, 2026

Copy link
Copy Markdown

Intent

Fix the fm-spawn failure where the reconcile-github-delivery-state scout never created metadata because the bare project name was resolved relative to the caller instead of the active Firstmate home's projects directory. Preserve the end-user-aligned reproduction and distinguish the initiating trigger, masking condition, and visible symptom. Make bare project names resolve to the intended home-scoped primary project clone so treehouse or Orca can create a genuine isolated disposable worktree, while retaining or strengthening the existing fail-closed assertion that refuses a primary-checkout target and providing a clear diagnostic when the project cannot resolve. Preserve already-working absolute, relative-path, and projects/ spawns across all supported runtime backends. Turn the exact reconcile-github-delivery-state scout failure into an executable regression that proves the published worktree is a distinct linked-worktree root and that a caller cwd shadow cannot capture the bare project name. Keep the Firstmate shared tracked change minimal and surgical, follow Firstmate coding guidelines, validate through no-mistakes to a green PR, and do not merge because this Firstmate-repo PR waits for the captain's explicit merge word.

What Changed

  • Resolve bare project names and projects/<name> strictly from the active Firstmate home's projects directory, with a clear error when the project is missing.
  • Preserve absolute and explicit relative path handling, including . and .., while preventing caller-directory shadows from capturing bare names.
  • Add spawn regressions covering the original scout failure, linked-worktree isolation, shadow handling, missing projects, help text, and relative paths.

Risk Assessment

✅ Low: Captain, the shared resolver now enforces home-only bare-name lookup, preserves explicit path forms, and the regressions cover the original failure, cwd masking, missing-project refusal, and worktree isolation invariant.

Testing

No baseline test results were supplied. Targeted treehouse-facing, Orca, and backend-dispatch tests passed; after the initial manual capture correctly hit gate-agent protection, its isolated retry used the repository's test-only bypass and proved home-scoped resolution, shadow immunity, distinct linked-worktree publication, path compatibility, and clear fail-closed diagnostics. A CLI transcript was captured instead of visual evidence because this change has no UI surface.

Evidence: Exact scout CLI and persisted-state evidence

spawned reconcile-github-delivery-state harness=codex kind=scout ... verified: published worktree is a distinct linked-worktree root verified: caller cwd shadow did not capture the bare project name Overall: PASS

Scenario: exact reconcile-github-delivery-state scout with a caller cwd shadow
Caller cwd: /tmp/tmp.iGFxOld1C8/caller
Caller shadow: /tmp/tmp.iGFxOld1C8/caller/analytics
Home project: /tmp/tmp.iGFxOld1C8/home/projects/analytics

$ fm-spawn.sh reconcile-github-delivery-state analytics --scout --harness codex --model gpt-5.6-sol --effort medium
spawned reconcile-github-delivery-state harness=codex kind=scout window=firstmate:fm-reconcile-github-delivery-state worktree=/tmp/tmp.iGFxOld1C8/analytics-linked-worktree
exit_status=0

Published metadata:
worktree=/tmp/tmp.iGFxOld1C8/analytics-linked-worktree
project=/tmp/tmp.iGFxOld1C8/home/projects/analytics
harness=codex
kind=scout
model=gpt-5.6-sol
effort=medium

Git worktree roots:
worktree /tmp/tmp.iGFxOld1C8/home/projects/analytics
worktree /tmp/tmp.iGFxOld1C8/analytics-linked-worktree
verified: published worktree is a distinct linked-worktree root
verified: caller cwd shadow did not capture the bare project name

Compatible project argument forms from the same caller cwd:
arg=projects/analytics exit=0 project=/tmp/tmp.iGFxOld1C8/home/projects/analytics worktree=/tmp/tmp.iGFxOld1C8/analytics-linked-worktree
arg=/tmp/tmp.iGFxOld1C8/home/projects/analytics exit=0 project=/tmp/tmp.iGFxOld1C8/home/projects/analytics worktree=/tmp/tmp.iGFxOld1C8/analytics-linked-worktree
arg=../home/projects/analytics exit=0 project=/tmp/tmp.iGFxOld1C8/home/projects/analytics worktree=/tmp/tmp.iGFxOld1C8/analytics-linked-worktree

Missing home project with a same-name caller shadow:
error: project directory cannot be resolved: analytics (tried '/tmp/tmp.iGFxOld1C8/missing-home/projects/analytics')
exit_status=1 metadata_exists=no

Primary-checkout target refusal:
error: treehouse get did not yield an isolated worktree (resolved '/tmp/tmp.iGFxOld1C8/home/projects/analytics/subdir'; worktree root '/tmp/tmp.iGFxOld1C8/home/projects/analytics'; primary '/tmp/tmp.iGFxOld1C8/home/projects/analytics'); refusing to launch to avoid tangling the primary checkout. Inspect target firstmate:fm-primary-checkout-refusal
exit_status=1 metadata_exists=no

Overall: PASS

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed (2) ✅
  • 🚨 bin/fm-spawn.sh:1493 - Required criterion: "Preserve already-working absolute, relative-path, and projects/<name> spawns across all supported runtime backends." The new no-slash branch treats . and .. as home-scoped names. Because $PROJECTS/. and $PROJECTS/.. normally exist, these arguments now target the projects collection or Firstmate home instead of the caller-relative project, potentially spawning from the wrong repository. Handle . and .. as relative paths before the bare-name branch and add focused regressions.
  • 🚨 tests/fm-tangle-guard.test.sh:242 - Required criteria say to "Turn the exact reconcile-github-delivery-state scout failure into an executable regression" and preserve the reproduction's trigger, masking condition, and visible symptom. This fixture creates $caller/analytics before its only invocation, so the base implementation resolves that shadow and writes metadata instead of reproducing the reported no-metadata failure. Keep the shadow-precedence case, but add a separate no-shadow invocation that reproduces the original sequence and explicitly identifies the trigger, mask, and symptom.

🔧 Fix: Captain, preserve relative paths and split spawn regressions
1 error still open:

  • 🚨 bin/fm-spawn.sh:1497 - The required criterion says, "Make bare project names resolve to the intended home-scoped primary project clone" and requires that "a caller cwd shadow cannot capture the bare project name." However, the changed elif [ -d &#34;$path&#34; ] branch still resolves a bare name from the caller when the home clone is absent. A caller-side analytics/ directory can therefore capture the name and bypass the required clear missing-project diagnostic. Remove this fallback for bare names while retaining the explicit .|.. and */* relative-path cases, update the help text, and add a missing-home plus cwd-shadow regression.

🔧 Fix: Captain, enforce home-only bare project resolution
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-tangle-guard.test.sh
  • bash tests/fm-backend-orca.test.sh
  • bash tests/fm-backend.test.sh
  • Manual temporary-home E2E: bin/fm-spawn.sh reconcile-github-delivery-state analytics --scout --harness codex --model gpt-5.6-sol --effort medium, executed from a caller directory containing a competing analytics repository.
  • Manual path matrix for projects/analytics, an absolute project path, and ../home/projects/analytics, plus missing-project and primary-checkout refusal counterfactuals.
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)
✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge, with no blocking failure remaining.

The previously reported relative-path, cwd-shadow, and exact-reproduction issues are addressed by the resolver ordering, home-only bare-name lookup, explicit diagnostics, and focused regressions; no blocking failure remains.

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

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Scheduled 7:10pm PT 8/23 pass. VISION.md read in full from current main 7b88520c055408a18f1476ecce08be60b2885fc9 (#2858). First look at this unstamped PR. No captain comment authorizing a merge.

VISION (inspected resolve_project_dir_arg and the cd failure path in bin/fm-spawn.sh, plus the new cases in tests/fm-tangle-guard.test.sh: no-shadow reproduction, cwd-shadow immunity, missing-home refusal, ./.. caller-relative, help text). Per-rule:

  • One captain, one interface: aligns (wrong-project spawn is a silent failure; home-scoped resolution plus a named error is the honest path).
  • Authority is explicit and never inferred: aligns (bare names bind to the home registry already used by projects/<name>; ., .., and any slash path keep caller-supplied semantics).
  • Scripts own the mechanics: aligns (path case analysis, no agent guess).
  • A restart is a non-event: cannot tell (spawn-time resolver; no durable record change).
  • Delegation with a spine: aligns (isolated-worktree guard remains; missing home project refuses a same-name cwd shadow instead of launching it).
  • The fleet outlives any vendor: aligns (resolver is backend-agnostic).
  • Scope: aligns (spawn resolver + colocated regressions).

Class: corrective (completes the existing home-registry identity for slash-free project names; cwd-relative bare names were a shadow bug, not a supported contract). Not default-behavior: explicit path forms are unchanged, and the isolated-worktree refusal still stands.

Security review of the FULL diff before first-fork CI approval: files are only bin/fm-spawn.sh and tests/fm-tangle-guard.test.sh. No .github/workflows changes, no secrets, no credentials, no exfil. Approving waiting workflow runs.

Workflow-approval this pass (captain gh):

  • 32680920875 CI — approved; now in_progress
  • 32680920918 Require no-mistakes (opened) — approved; completed failure (PR body compliance; no no-mistakes-pipeline-attestation:v1)
  • 32682278706 Require no-mistakes (edited) — approved; completed failure (same body-compliance gap)

MERGEABLE / UNSTABLE, ahead 3 / behind 0. HEAD 36a5534e260195e5da4afb28cc292f51cf1b0d82. Body has no pipeline attestation.

Overlap: bin/fm-spawn.sh is on standing spawn-freshen / pool-isolation holds #2622 / #2693 / #2154 / #2804 (all OPEN). tests/fm-tangle-guard.test.sh also overlaps #2804. Any PR editing fm-spawn.sh stays held.

Waiting on the author for a HEAD-matching no-mistakes-pipeline-attestation:v1 (Require no-mistakes already failed body compliance after approval). Spawn overlap is a captain/coordinator hold, not something the author can clear. Not flagging Firstmate: author/CI blockers remain, and the spawn hold would still block even after NM is stamped. Do not rebase.

Land-eligible: NO. Captain-flag NOW: no.

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