Skip to content

feat: add fixed-home primary launcher selectors#879

Open
LucasOl1337 wants to merge 13 commits into
kunchenguid:mainfrom
LucasOl1337:fm/firstmate-primary-launcher-selectors-m3
Open

feat: add fixed-home primary launcher selectors#879
LucasOl1337 wants to merge 13 commits into
kunchenguid:mainfrom
LucasOl1337:fm/firstmate-primary-launcher-selectors-m3

Conversation

@LucasOl1337

@LucasOl1337 LucasOl1337 commented Jul 22, 2026

Copy link
Copy Markdown

Intent

Let the captain launch the same fixed Firstmate operational home and durable memory from Windows PowerShell using bare firstmate or explicit --codex, --pi, --grok, --claude, and --opencode selectors, with optional harness-supported model and effort axes. Preserve bare Codex's backwards-compatible unrestricted posture while keeping alternate harness permissions explicit and normal, use a fixed launcher-owned Grok instruction to run session start exactly once, reject ambiguous or unsafe input, and prevent a divergent harness from attaching to or replacing an active same-home primary. Keep WSL distribution Ubuntu, Linux user firstmate, repository root and FM_HOME /home/firstmate/firstmate, tmux identity, and session lock fixed. Include a safe recoverable PowerShell Windows wrapper and an explicit cmd.exe refusal shim; all supported launch forms are PowerShell-only. Do not modify live local launchers or global harness configuration in the tracked change. Produce a draft PR and do not merge.

What Changed

  • Add a fixed-home tmux primary launcher with Codex, Pi, Grok, Claude, and OpenCode selectors plus supported model and effort options.
  • Verify harness processes, authoritative lock ownership, and compatible session metadata before attaching or launching, refusing divergent same-home sessions and unsafe inputs.
  • Add PowerShell-only Windows launch wrappers, documentation, and cross-platform test coverage for launcher selection, forwarding, locking, and harness detection.

Risk Assessment

✅ Low: The focused fix safely removes the undeclared Python dependency using equivalent read-only process inspection without changing launcher behavior.

Testing

Diff inspection, focused automated tests, real isolated tmux launch/attach/refusal flows, native Windows PowerShell validation, manual CLI evidence, and draft-PR verification all succeeded. Herdr-only AFK coverage skipped because Herdr is unavailable, but the relevant tmux end-to-end path passed. No screenshot was produced because this is a CLI/PowerShell change with no rendered UI; reviewer-visible transcripts capture the actual surface.

Evidence: Focused behavior test transcript
FM_TEST_BEGIN 2026-07-22T22:39:01Z tests/fm-primary-launch.test.sh family=session-bootstrap expected_gate_skip=none
ok - tracked primary launcher and ownership helpers are executable
ok - primary launcher rejects ambiguous and unsupported input
ok - all primary selectors preserve identity and map only model and effort argv
ok - bare and matching selectors attach while divergent selectors refuse
ok - live locks refuse and stale locks remain for session-start authority
ok - concurrent launch attempts serialize to one primary session
ok - fm-lock live-pid recognizes supported harnesses without mutation
ok - verified interpreter-hosted package entrypoints preserve harness identities
ok - interpreter-hosted lookalikes do not satisfy harness ownership
ok - native harness ownership rejects spoofed argv names
ok - native harness validation follows symlinks before rejecting Windows targets
ok - existing sessions require matching home, lock, metadata, and live process
FM_TEST_END 2026-07-22T22:39:13Z tests/fm-primary-launch.test.sh exit=0 duration_ms=11279 gate_skip=false
FM_TEST_BEGIN 2026-07-22T22:39:13Z tests/fm-primary-windows.test.sh family=pure-contract-unit expected_gate_skip=none
ok - PowerShell preserves opaque argv and CMD refuses without parsing it
FM_TEST_END 2026-07-22T22:39:13Z tests/fm-primary-windows.test.sh exit=0 duration_ms=568 gate_skip=false
FM_TEST_BEGIN 2026-07-22T22:39:13Z tests/fm-grok-harness.test.sh family=pure-contract-unit expected_gate_skip=none
ok - grok global hook requires a firstmate registry token
ok - grok teardown removes pointer and token state
ok - fm-lock recognizes grok harness processes
FM_TEST_END 2026-07-22T22:39:18Z tests/fm-grok-harness.test.sh exit=0 duration_ms=5110 gate_skip=false
FM_TEST_BEGIN 2026-07-22T22:39:18Z tests/fm-afk-launch.test.sh family=real-herdr-gated expected_gate_skip=herdr
ok - clear-stale: removes escalations buffer, sidecar, and wedge marker
ok - clear-stale: leaves the durable wake-queue intact (no pending work dropped)
ok - refresh: daemon already alive - stale artifacts preserved (current session's buffer kept)
ok - stop-ordering: daemon SIGTERM'd while .afk still present (flush is not a no-op)
ok - stop-ordering: .afk cleared last
ok - stop-ordering: daemon-terminal record removed
ok - stop identity: stale lock cannot signal an unrelated live process
ok - failed start: away flag and delivery artifacts roll back
ok - concurrent start: one serialized daemon terminal remains tracked
ok - launcher lock: incomplete publication receives initialization grace
ok - launcher signal: TERM exits and releases the lifecycle lock
fm-afk-launch: daemon launched in non-visible herdr workspace ws-partial (pane lab:pane-exact), supervising lab:captain
ok - herdr create: malformed response recovers durable exact ownership
fm-afk-launch: herdr create failed after returning exact ids; closing lab:pane-exact
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - herdr create error: unconfirmed exact id is persisted for reconciliation
fm-afk-launch: failed to run daemon in herdr pane lab:pane-exact; closing it
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - herdr run failure: unconfirmed exact id remains reconcilable
fm-afk-launch: failed to persist daemon terminal record; closing tmux:exact-session
ok - record failure: newly created terminal is closed by exact id
fm-afk-launch: daemon did not become ready; closing tmux:exact-session
ok - readiness failure: exact terminal and durable record roll back
fm-afk-launch: daemon did not become ready; closing tmux:exact-session
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - readiness failure: unconfirmed terminal retains its reconciliation id
ok - tmux absence: clean missing differs from transport probe failure
ok - native lifecycle: launcher owns state with no terminal
ok - native lifecycle: uniform stop clears state without closing a terminal
ok - native entry: launcher-prepared lifecycle state is not rewritten
fm-afk-launch: reconciling leaked daemon terminal tmux:exact-session
fm-afk-launch: recorded terminal teardown is unconfirmed; preserving exact id
ok - teardown failure: exact terminal record is preserved
ok - record publication: failed atomic rename preserves the complete prior record
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
ok - record read: malformed record fails closed without acting on a partial id
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
fm-afk-launch: malformed daemon terminal record; refusing to stop away mode
ok - stop: malformed terminal record preserves away state and fails closed
fm-afk-launch: failed to create detached tmux daemon session 'fm-afk-daemon-2095291142-71030-12487-1784759962'
ok - tmux launch: planned exact target is recorded before creation and removed on failure
fm-afk-launch: failed to create detached tmux daemon session 'fm-afk-daemon-1517057139-71059-28930-1784759962'
ok - tmux launch: unique names eliminate collision teardown
ok - stop validation: malformed record causes no daemon or state side effects
ok - launcher lock: incomplete metadata fails acquisition and releases lock
fm-afk-launch: failed to clear away-mode flag
fm-afk-launch: away mode stopped; terminal teardown remains recorded for retry
ok - stop state: away-flag removal failure is surfaced
fm-afk-launch: away-mode daemon did not exit after SIGTERM; preserving lifecycle state
ok - stop liveness: captured live daemon preserves lifecycle state after lock release
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
fm-afk-launch: daemon terminal record is malformed; refusing to act on it
ok - refresh record: malformed terminal identity fails closed
fm-afk-launch: failed to clear stale away-mode artifacts
ok - clear failure: native entry aborts and restores prior state
fm-afk-launch: reconciling leaked daemon terminal tmux:exact-session
fm-afk-launch: terminal close command failed, but exact absence was confirmed
ok - confirmed absence: cleanup succeeds and removes the stale record
fm-afk-launch: rollback restoration incomplete; backup retained at /tmp/fm-afk-restore-fail.2SN28M/state/.afk-launch-backup.o3Jknd
ok - rollback restore: incomplete restoration retains its recovery backup
ok - flag failure: lifecycle aborts without active state
skip: herdr not found (herdr e2e)
ok - tmux e2e: captain window pane count unchanged after start (no split-window)
ok - tmux e2e: daemon launched in a separate detached session
ok - tmux e2e: captain window pane count unchanged after stop
ok - tmux e2e: daemon session killed by exact id on stop
ok - tmux e2e: record + .afk cleared on stop
FM_TEST_END 2026-07-22T22:39:23Z tests/fm-afk-launch.test.sh exit=0 duration_ms=4515 gate_skip=false
FM_TEST_BEGIN 2026-07-22T22:39:23Z tests/fm-test-run.test.sh family=pure-contract-unit expected_gate_skip=none
ok - exact suite coverage: --all lists every tests/*.test.sh once
ok - family selection returns a proper subset of the suite
ok - single-script selection lists exactly that path
ok - changed-file selection stays conservative (never silent full suite)
ok - changed selection covers dependents and fails closed for unmapped source
ok - empty changed selection emits deterministic text and JSON summaries
ok - timing markers and JSON artifact are valid
ok - aggregate exit reflects any script failure
ok - gate-skip accounting is honest and non-failing
ok - fail-on-gate-skip converts herdr-not-found into a hard failure
ok - exclude-family drops the named primary family after selection
ok - CI and CONTRIBUTING call the one-owner runner; no full-suite local Test
ok - portable shard union, disjointness, and coverage guard hold
ok - --jobs refuses non-proven / stateful selections
ok - jobs scheduler runs proven scripts; failure propagates; non-proven refused
ok - aggregate-json merges lane timing artifacts
FM_TEST_END 2026-07-22T22:39:38Z tests/fm-test-run.test.sh exit=0 duration_ms=14575 gate_skip=false
FM_TEST_SUMMARY total=5 failed=0 skipped_gate=0 duration_ms=36328
FM_TEST_SUMMARY_FAMILY family=pure-contract-unit count=3 duration_ms=20253 failed=0
FM_TEST_SUMMARY_FAMILY family=real-herdr-gated count=1 duration_ms=4515 failed=0
FM_TEST_SUMMARY_FAMILY family=session-bootstrap count=1 duration_ms=11279 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-test-run.test.sh duration_ms=14575
FM_TEST_SLOWEST rank=2 script=tests/fm-primary-launch.test.sh duration_ms=11279
FM_TEST_SLOWEST rank=3 script=tests/fm-grok-harness.test.sh duration_ms=5110
FM_TEST_SLOWEST rank=4 script=tests/fm-afk-launch.test.sh duration_ms=4515
FM_TEST_SLOWEST rank=5 script=tests/fm-primary-windows.test.sh duration_ms=568
- Evidence: End-user CLI experience: help, unsafe-input rejection, native PowerShell argv validation, and cmd.exe refusal (local file: /tmp/no-mistakes-evidence/01KY5ZGGSH6HK1YJRY4T59538N/cli-user-experience.txt)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed ✅
  • 🚨 windows/firstmate.ps1:12 - Intent requires PowerShell to preserve arbitrary model/effort values as opaque arguments, but & wsl.exe @wslArgs passes them through PowerShell's native-command serialization, which can alter embedded quotes or empty arguments on Windows PowerShell and legacy PowerShell 7 modes. The added test only copies values between PowerShell arrays and never exercises the wsl.exe boundary. Use a lossless transport such as encoded arguments, or explicitly reject values that cannot be forwarded safely.
  • ⚠️ bin/fm-harness-process.sh:12 - Harness identification now unconditionally invokes python3, although Python is not checked or documented as a launcher prerequisite. On an otherwise supported WSL installation without Python, every lock holder appears stale and new sessions cannot acquire the authoritative lock, causing the launcher to kill them after its timeout. Use readlink directly or fail early with an actionable prerequisite error.

🔧 Fix: Remove Python dependency from harness process identification
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Inspected git diff 554983438606101316cb23726bdf80d4ae2f9a6b..90155f296a82995ae4e036995c8b312787eb39aa against the supplied user intent.
  • bin/fm-test-run.sh tests/fm-primary-launch.test.sh tests/fm-primary-windows.test.sh tests/fm-grok-harness.test.sh tests/fm-afk-launch.test.sh tests/fm-test-run.test.sh
  • bin/fm-test-run.sh --list --changed --base 554983438606101316cb23726bdf80d4ae2f9a6b
  • Manual CLI checks: bin/fm-primary-launch.sh --help, conflicting selectors, model without selector, native powershell.exe -File tests/fm-primary-windows.test.ps1, and cmd.exe /c windows\firstmate.cmd.
  • gh-axi pr view 879 verified the PR is open, draft, and unmerged.
  • git status --short verified testing left the worktree clean.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@LucasOl1337
LucasOl1337 marked this pull request as ready for review July 22, 2026 23:28
@kunchenguid

kunchenguid commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Automated reminder: thanks for the PR! This branch currently has a merge conflict with the base branch.

When you get a chance, please rebase onto (or merge) the latest base branch, resolve the conflict, and push. After that, checks will re-run and the PR will get looked at again.

Noted for firstmate#879 at 61b8458d.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants