Conversation
d21eeec to
fe00911
Compare
|
Moving this to draft — please don't spend review time on it yet. On re-examination the test coverage here is weaker than it looks. The 12+14 checks assert registration-level facts ("the launch template is the verified argv-seed line", "cline is recognized as a known bare adapter name", "effort maps to --thinking"), and the harness facts in I'd rather prove that before asking you to merge it. Marking ready again once a live dispatch acceptance passes for both adapters. The other open PRs are unaffected — #1217 (federated fleet) was accepted against three real operators with a real concurrent claim race and live quota, and #1142's Herdr behaviour suites drive a real Herdr lab. |
Adds cline as a verified FirstMate harness adapter. Every fact captured
empirically from live `cline -i --tui` panes (docs/verification/cline-adapter.md).
- fm-spawn.sh: launch_template (argv-seed `-i --tui --auto-approve true`),
--model + effort->--thinking mapping, known-adapter allowlists
- fm-harness.sh: detect_own ancestry match (*cline*, comm + args)
- fm-tmux-lib.sh: busy signature `esc to cancel` (distinct from `esc to interrupt`)
- backends/{herdr,cmux,orca}.sh: IDLE_RE default covers cline's non-dim
placeholders (What can I do for you? / Ask anything...)
- harness-adapters SKILL: cline knowledge section
- tests/fm-cline-harness.test.sh: 12 behavior checks (all green)
Composer/ghost/grok/secondmate-harness regressions stay green; the 5 existing
launch templates stay byte-pinned. Full live end-to-end crewmate dispatch through
herdr remains the closing acceptance step (documented).
…apter
Adds cursor-agent as a verified FirstMate harness adapter. Every fact captured
empirically from live `cursor-agent --force` panes (docs/verification/cursor-agent-adapter.md).
- fm-spawn.sh: launch_template (argv-seed `--force`), --model allowlist (effort is a
model bracket param, no flag), known-adapter allowlists
- fm-harness.sh: detect_own ancestry match (*cursor* -> cursor-agent, comm + args)
- fm-tmux-lib.sh: busy signature `ctrl+c to stop` (distinct from pi's `Working...`)
- backends/{herdr,cmux,orca}.sh: IDLE_RE default covers cursor's → placeholders
- harness-adapters SKILL: cursor-agent knowledge (incl. the workspace-trust gate:
--trust is headless-only; bypass via pre-seeded .workspace-trusted or `a`)
- tests/fm-cursor-agent-harness.test.sh: 12 checks (green); de-brittled the cline
test's allowlist asserts to tolerate trailing adapters
Regressions green (composer-lib 9, composer-ghost 30, grok 3, secondmate-harness 40,
kimi 19; 5 existing launch templates byte-pinned). Trust-gate readiness wiring in
fm-spawn + full live herdr dispatch remain the closing acceptance step (documented).
…ds; wire cursor trust gate
…ss and composer defaults
The shared FM_COMPOSER_BARE_PROMPT_RE_DEFAULT this branch introduces was '^[❯›→]', a bracket expression. Under the fleet's C/POSIX locale grep matches bracket expressions BYTE-wise, so [❯›→] decomposes into the shared leading UTF-8 byte (0xE2) and spuriously matches any multibyte glyph in that range - including box-drawing corners like ╰, which misclassifies a bordered composer's bottom border as a bare prompt row. bin/backends/herdr.sh already carried an alternation for exactly this reason, with the rationale in a comment. Centralising the constant without carrying that form would have reintroduced the bug fleet-wide, for every adapter, at the moment the default became shared. The constant is now '^(❯|›|→)' with the rationale beside it, so a later edit cannot quietly restore the bracket form. Verified: cline 12 ok, cursor-agent 14 ok, composer-lib 9 ok, composer-ghost 30 ok, lint 0.
Intent
Crewmate harness adapters for cline and cursor-agent (PR #1104), rebased onto upstream's pi-signed adapter: empirically verified launch templates, busy/idle signatures, interrupt/exit semantics, and backend idle-regex extensions, with 12-check suites per adapter. Goal: ship through the sanctioned pipeline so PR #1104 carries the no-mistakes signature and passes the Require-no-mistakes gate.
What Changed
bin/fm-harness.sh, launch templates and adapter allowlists inbin/fm-spawn.sh(including Cursor's workspace-trust gate predicates), and idle-regex extensions across the herdr, cmux, and orca backends.bin/fm-composer-lib.sh/bin/fm-tmux-lib.shso all backends recognize the new adapters' idle composers (e.g. cline's "Ask anything..." and cursor's "→ Plan, search, build anything") instead of each backend carrying its own copies.tests/fm-cline-harness.test.sh12 checks,tests/fm-cursor-agent-harness.test.sh14 checks) plus verification evidence docs underdocs/verification/, and updated the harness-adapters SKILL.md and backend/configuration docs to register the new adapters.Risk Assessment
✅ Low: The fix commit resolves all three round-1 warnings at the recommended shared boundary (fleet-wide idle/glyph defaults consumed by all four adapters, shared busy-default extension, and a kimi-pattern trust-gate readiness step that fails loudly), preserves the dead-shell safety rule with new test coverage, and leaves only informational nits and the honestly documented live end-to-end acceptance step.
Testing
Ran the two new adapter suites (26 checks total, all pass) plus regression suites for every shared file the change touched (composer lib, tmux busy matcher, grok/kimi adapters, cmux/herdr/orca backends — all pass), then demonstrated the adapters end-to-end against the real installed CLIs at the exact verified versions: live tmux pane captures show cline's
❯ What can I do for you?idle surface and cursor-agent's Workspace Trust dialog →a→→ Plan, search, build anythingidle composer, with the shipped busy/idle classifiers reading each live row correctly and cursor's self-written trust marker matching fm-spawn's pre-seed byte-shape;/quitexit verified. The end-user surface here is terminal panes, and the captured pane text is the exact surface the adapters consume (tmux capture-pane), so the pane captures are the reviewer-visible visual evidence; the only failure seen (secondmate suite) reproduces at base with CLAUDECODE=1 inherited from the session env and passes with it unset — pre-existing, not from this change. Mid-turn busy/interrupt semantics were not re-exercised live since that requires running real autonomous agent turns; those literals are covered by the suites against docs/verification captures.Evidence: Live adapter verification transcript
Evidence: Live cline 3.0.46 idle TUI pane capture (verified ❯ + placeholder surface)
❯ What can I do for you? ClinePass: Laguna S 2.1 (free) (high) ○ Plan ● Act (Tab) shipped busy-matcher: not busy · shipped classifier: empty · typed text → pendingEvidence: Live cursor-agent Workspace Trust dialog capture (the gate fm-spawn keys on)
⚠ Workspace Trust Required Do you trust the contents of this directory? /tmp/fm-adapter-e2e/cursor-ws ▶ [a] Trust this workspace gate predicates: dialog_present=TRUE, past_trust=FALSE → sent 'a'Evidence: Live cursor-agent post-trust idle composer capture
Cursor Agent v2026.07.23-e383d2b → Plan, search, build anything gate predicate past_trust=TRUE · busy-matcher: not busy · classifier: empty · /quit exits cleanlyEvidence: Live cline typed-input pane capture (pending classification)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
.agents/skills/harness-adapters/SKILL.md:411- cline's idle placeholder is not recognized on the tmux backend (the default session provider). SKILL.md:411 states the placeholder survives the ghost stripper (truecolor 131;137;140, luma ~136 > 128 cutoff, drawn bold) and that "the backend IDLE_RE default lists both placeholders so an empty cline composer reads empty" — but the extension was applied only to herdr/cmux/orca. The tmux classifier (fm_tmux_composer_row_state, bin/fm-tmux-lib.sh:159) passes only the unset-by-default FM_COMPOSER_IDLE_RE override, so an idle cline composer ('❯ Ask anything...') classifies as pending on tmux. Reachable consequences: (a) the away-mode pre-injection guard (pane_input_pending → fm_backend_composer_state tmux) defers escalations indefinitely — the documented afk-herdr-false-pending overnight-wedge class; (b) fm_tmux_submit_core never observes 'empty' after delivering text to a cline crewmate, and its fallback fm_pane_is_busy is called WITHOUT a harness argument so it uses FM_TMUX_BUSY_REGEX_DEFAULT (bin/fm-tmux-lib.sh:80), which lacks cline's 'esc to cancel' — every tmux text delivery to a cline crewmate reports unconfirmed ('pending') even when submission succeeded. Recommended shared boundary: add a per-harness idle-placeholder default to the tmux classifier mirroring the existing per-harness busy-regex map (or extend the shared strip/idle path), rather than a fourth backend-local patch. Marked ask-user because restricting cline supervision to the herdr backend may be the author's deliberate scope..agents/skills/harness-adapters/SKILL.md:433- cursor-agent's idle-empty proof is unreachable in every backend where it was added, contradicting SKILL.md:433 ("Matched as empty via the backend IDLE_RE (the glyph-prefixed placeholder form)"). The new IDLE_RE alternates '→ Plan, search, build anything|→ Add a follow-up' (bin/backends/herdr.sh:1830, cmux.sh:544, orca.sh:266) are only consulted after a structural finder promotes a composer row, but herdr promotes only bordered rows, bare rows matching FM_BACKEND_HERDR_BARE_PROMPT_RE='^[❯›]' (herdr.sh:1834), or pi separator pairs; cmux/orca promote side-bordered rows only. Cursor's composer is an unbordered '→'-prefixed row per the verification capture, so composer_state returns 'unknown' — never 'empty' — in all three backends, and the tmux path likewise yields unknown/pending (no idle default, '→' is not a recognized glyph or composer edge). Any consumer requiring proven-empty (injection guard, cmux/orca submit-confirm loops) wedges or defers for cursor crewmates. Note also cline's IDLE_RE entries are reachable only in herdr (cmux/orca require side borders cline does not draw). Fix at the structural boundary — e.g. add the verified '→' (U+2192) agent glyph to bare-row promotion and the shared glyph classifier — weighed against the dead-shell-prompt safety rule; ask-user because leaving the glyph classifier untouched was an explicit design choice in this change.bin/fm-spawn.sh:472- cursor-agent is selectable as a crew adapter (launch template at bin/fm-spawn.sh:472 plus the bare-name allowlists at lines 392/601) while the workspace-trust readiness wiring is absent: nothing pre-seeds ~/.cursor/projects/<slug>/.workspace-trusted or sends 'a' after the readiness gate, and --force/--trust do not bypass the interactive dialog. A crewmate spawned into an untrusted directory blocks on the trust dialog and the argv-seeded brief never auto-runs. The change documents this honestly as the remaining acceptance step (docs/verification/cursor-agent-adapter.md, SKILL.md), so this is authorized containment, not a hidden defect — but consider either wiring the readiness step or having fm-spawn refuse/warn on cursor-agent until it lands, so a captain setting config/crew-harness=cursor-agent today does not get a silently hung spawn.bin/fm-harness.sh:57- Detection breadth: detect_own matches any ancestor whose command name contains 'cursor' (bin/fm-harness.sh:57, and args-substring match at line 70). The Cursor IDE desktop process is literally named 'cursor', so fm-harness.sh run from a bare terminal inside Cursor IDE (no interposed agent harness in the 8-hop ancestry walk) reports own=cursor-agent instead of unknown, and config/crew-harness 'default' would then resolve crew spawns to cursor-agent. Similarly the node-args branch matches any script path containing 'cline'/'cursor' substrings. This mirrors the pre-existing 'grok' fragility pattern, but 'cursor' has a much higher ambient-collision probability. When a real harness is present it appears earlier in the walk, so practical impact is limited; noting the tradeoff.bin/fm-spawn.sh:392- Partial-wiring asymmetry, documented as deliberate: fm-spawn's --secondmate parse now accepts cline|cursor-agent as bare adapter names (bin/fm-spawn.sh:392) even though the SKILL states neither is wired for secondmate launches — bin/backends/tmux.sh:184's agent-process liveness list and bin/fm-session-lock-lib.sh:12's FM_HARNESS_RE were intentionally not extended. A cline/cursor secondmate would therefore spawn, but bootstrap recovery classifies a dead one as 'unverified-harness' and will not auto-respawn it. The allowlist inclusion is needed for correct argument parsing (otherwise the name would be misparsed as a FIRSTMATE_HOME path), and the degraded recovery behavior fails conservative; informational only.🔧 Fix: share composer idle/glyph defaults across backends; wire cursor trust gate
4 infos still open:
docs/verification/cursor-agent-adapter.md:97- Stale test count after the fix commit: docs/verification/cursor-agent-adapter.md:97 still says "12 behavior checks (all green)" but tests/fm-cursor-agent-harness.test.sh now runs 14 checks (the fix added test_cursor_glyph_is_promoted_and_safe and test_cursor_trust_gate_wired). The cline doc's count (12) is still accurate. The intent's "12-check suites per adapter" is met and exceeded, so this is only a doc-accuracy nit — update the table cell to 14.bin/backends/orca.sh:267- Acknowledged tradeoff of the round-1 fix: cmux/orca now promote bare agent-glyph rows (❯ › →) for every harness, so a stray glyph-prefixed transcript line in the scan window (orca reads up to 200 lines) can be promoted as the composer where previously only bordered rows were. The failure direction is safe — the last match wins so the bottom-anchored live composer normally outranks transcript rows, a promoted stray row classifies pending/unknown which blocks injection, and a lone bare glyph reading empty mirrors the pre-existing accepted ❯/› semantics on herdr — but it is a behavior widening for existing harnesses on those backends, not just the new adapters. Informational; no action needed.bin/fm-harness.sh:57- Still present from round 1 (informational, unchanged by the fix commit): detect_own matches any ancestor command name containing 'cursor' (bin/fm-harness.sh:57, args-substring at line 70). The Cursor IDE desktop process is literally named 'cursor', so fm-harness.sh run from a bare terminal inside Cursor IDE with no interposed harness reports own=cursor-agent instead of unknown, and crew-harness 'default' would resolve crew spawns accordingly. Mirrors the pre-existing 'grok' fragility; when a real harness is present it appears earlier in the ancestry walk, limiting practical impact.bin/fm-spawn.sh:392- Still present from round 1 (informational, documented as deliberate): fm-spawn's --secondmate parse accepts cline|cursor-agent as bare adapter names (bin/fm-spawn.sh:392) while neither is wired for secondmate launches — bin/backends/tmux.sh:184's liveness list and bin/fm-session-lock-lib.sh:12's FM_HARNESS_RE were intentionally not extended. A cline/cursor secondmate would spawn but bootstrap recovery classifies a dead one as 'unverified-harness' and will not auto-respawn it; the allowlist entry is needed for correct argument parsing and the degraded recovery fails conservative.tests/fm-secondmate-harness.test.sh:162- Pre-existing, environment-dependent: tests/fm-secondmate-harness.test.sh fails when run inside a Claude Code session because the inherited CLAUDECODE=1 env var makes bin/fm-harness.sh:38 short-circuit to 'claude' before the test's faked pi ancestry is consulted. It fails identically at the base commit fa0d85d (verified via git archive) and passes fully withenv -u CLAUDECODE. Not caused by this change; noted so gate/CI runners embedded in Claude Code sessions aren't surprised.bash tests/fm-cline-harness.test.sh— 12/12 checks passbash tests/fm-cursor-agent-harness.test.sh— 14/14 checks passbash tests/fm-composer-lib.test.shandbash tests/fm-tmux-submit-busy.test.sh— pass (shared classifier/busy-matcher regression)bash tests/fm-grok-harness.test.shandbash tests/fm-kimi-harness.test.sh— pass (sibling adapters unaffected)bash tests/fm-backend-cmux.test.sh,bash tests/fm-backend-herdr.test.sh,bash tests/fm-backend-orca.test.sh— pass (backends whose IDLE_RE defaults were rewired to the shared default)env -u CLAUDECODE bash tests/fm-secondmate-harness.test.sh— full pass; with CLAUDECODE set it fails identically at base commit fa0d85d (verified viagit archiveextraction), proving the failure is pre-existing and environment-induced, not from this changeManual live E2E (real CLIs at the verified versions, driven via a private tmux server): launchedcline -i --tui, captured the pane, ran the shippedfm_busy_lines_match clineandfm_composer_classify_contenton the live rows (placeholder → empty, typed text → pending, idle row → not busy)Manual live E2E: launchedcursor-agentin an untrusted scratch dir, captured the live 'Workspace Trust Required' dialog, verified fm-spawn'scursor_trust_dialog_present/cursor_pane_is_past_trustpredicates against the real captures, sent the verifiedakeypress to clear trust into the verified '→ Plan, search, build anything' idle composer, confirmed cursor's own.workspace-trustedmarker matches fm-spawn's pre-seed shape, and confirmed/quitexits cleanlyCleanup verified: test-created scratch state removed (trust marker dir, tmux server, /tmp scratch),git status --porcelaincleanbin/fm-spawn.sh:392- fm-spawn.sh's secondmate argument allowlist accepts cline/cursor-agent as explicit adapter names, but the harness-adapters SKILL.md (owner) states neither is wired for secondmate launches (no backends/tmux.sh liveness entry). Documentation follows the SKILL.md owner's statement (crewmate-only); the residual code/doc tension is in executable behavior, out of scope for this documentation phase.🔧 **Lint** - 1 issue found → auto-fixed ✅
🔧 Fix: annotate shared composer defaults for cross-file SC2034
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.