Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d3a7b24
feat(harness): add the cline crewmate/scout adapter with ClinePass di…
keenvc Sep 16, 2026
a5c4c53
feat(bin): add per-spawn Claude config-dir seat to fm-spawn.sh
keenvc Sep 19, 2026
6c8f420
no-mistakes(review): clarify Claude seat refusals for bypass dialog a…
keenvc Sep 19, 2026
e0a2119
no-mistakes(review): refuse Claude seat flag on remote secondmates; d…
keenvc Sep 19, 2026
ea88453
no-mistakes(review): cite verified bypass-store probe; untrack stray …
keenvc Sep 19, 2026
97606f2
no-mistakes(review): ignore .omc/ and untrack stray handoff artifact
keenvc Sep 19, 2026
2f9894e
no-mistakes(review): warn on every accepted Claude seat, simplify ref…
keenvc Sep 19, 2026
2273cba
no-mistakes(review): move seat guidance to docs; mark seated quota ca…
keenvc Sep 19, 2026
fb04277
no-mistakes(review): judge seated candidates through shared gate; dro…
keenvc Sep 19, 2026
bf0c3fd
no-mistakes(review): restore .omc/ ignore rule and untrack handoff ar…
keenvc Sep 19, 2026
8d2a85c
no-mistakes(review): restrict @seated quota marker to the claude harness
keenvc Sep 19, 2026
ce2a294
no-mistakes(review): revert @seated quota marker; keep only the spawn…
keenvc Sep 19, 2026
9b5b36c
no-mistakes(review): keep recorded Claude seat across bare secondmate…
keenvc Sep 19, 2026
30e7a05
no-mistakes(document): document remote seat refusal; correct stale se…
keenvc Sep 19, 2026
1f9beb7
feat: add human-text-discipline skill for human-read text
keenvc Sep 20, 2026
83dad80
feat: require a before/after evidence pair in the ship definition of …
keenvc Sep 20, 2026
2c1be1f
feat(bin): cap live lanes per billing provider at spawn
keenvc Sep 20, 2026
32386da
feat(bin): add recurring re-verification of aged captain holds
keenvc Sep 20, 2026
e798606
fix(bin): bound herdr CLI probes so a hung read cannot wedge a superv…
keenvc Sep 20, 2026
344485a
fix(bin): account for every registered secondmate in liveness sweep
keenvc Sep 20, 2026
8a78689
fix(bin): prove agy's empty composer by identity so control exit works
keenvc Sep 20, 2026
818d3f3
feat(bin): add cross-home work claims so two homes cannot race one ta…
keenvc Sep 20, 2026
eec178c
fix(bin): make a captain hold revoke standing merge authority
keenvc Sep 20, 2026
c6d1147
fix(bin): classify provider quota walls as a distinct quota state
keenvc Sep 20, 2026
da76467
fix(bin): retire cleared lane records and stop their wakes
keenvc Sep 20, 2026
2824db8
Merge PR #4984: feat: add human-text-discipline skill for human-read …
keenvc Sep 20, 2026
9f9b2e7
Merge PR #4985: require a before/after evidence pair in the ship defi…
keenvc Sep 20, 2026
d4de68b
Merge PR #4986: cap live lanes per billing provider at spawn
keenvc Sep 20, 2026
5e5b78e
Merge PR #4987: recurring re-verification of aged captain holds
keenvc Sep 20, 2026
2a8f04e
Merge PR #4988: bound herdr CLI probes so a hung read cannot wedge a …
keenvc Sep 20, 2026
a50e88b
Merge PR #4989: account for every registered secondmate in liveness s…
keenvc Sep 20, 2026
8a743ad
Merge PR #4990: prove agy's empty composer by identity so control exi…
keenvc Sep 20, 2026
c0c3f7e
Merge PR #4992: add cross-home work claims so two homes cannot race o…
keenvc Sep 20, 2026
3ed83b1
Merge PR #4993: make a captain hold revoke standing merge authority
keenvc Sep 20, 2026
6b6357a
Merge PR #4996: classify provider quota walls as a distinct quota state
keenvc Sep 20, 2026
b06cfcb
Merge PR #4997: retire cleared lane records and stop their wakes
keenvc Sep 20, 2026
246dbdf
Merge fork main: preserve the cline crewmate/scout adapter (fork PR #…
keenvc Sep 20, 2026
d9356ca
WIP: preserve pre-existing uncommitted local edits before pulling the…
keenvc Sep 20, 2026
6c95cea
Merge fork main: land the P2 firstmate reliability batch (10 PRs) plu…
keenvc Sep 20, 2026
c187c1b
feat(bin): add verified openhands crewmate/scout adapter
keenvc Sep 20, 2026
dce008a
Merge PR #5013: add verified openhands crewmate/scout adapter
keenvc Sep 20, 2026
66063a5
Merge branch 'fm/fm-claude-per-lane-seat' into local-fork-main
keenvc Sep 22, 2026
ab9c189
fix(bin): read aged holds via backlog-json, not contribution-input (#6)
keenvc Sep 28, 2026
e60c3c6
[ci] Get the fork's main green: fix the four standing CI failures (#8)
keenvc Sep 28, 2026
7f74031
[teardown] fix: allow stale task cleanup when a pool slot has a new o…
keenvc Sep 29, 2026
9928ab1
fix(bin): launch opencode v2 workers with --standalone and the model …
keenvc Sep 29, 2026
074e8ad
Merge upstream kunchenguid/firstmate main into the fork
keenvc Sep 29, 2026
a79543a
no-mistakes(review): Remove duplicate .omc/ entry from .gitignore
keenvc Sep 30, 2026
8669fe6
fix(bin): restore fleet-snapshot ARG_MAX file transport after upstrea…
keenvc Sep 30, 2026
39e648e
no-mistakes(test): Bound herdr CLI timeout in session start, fix test…
keenvc Sep 30, 2026
37aa85e
no-mistakes(document): Document herdr CLI timeout cap in session start
keenvc Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/agent-skill-trigger-index/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ These skills are not captain-invocable; load them only at their precise triggers
- `fmx-respond` - load on an `x-mention <request_id>` `check:` wake to handle the mention, on an `x-mode-error ...` `check:` wake to report the Relay configuration blocker, on a `public-followup ...` `check:` wake or a startup-surfaced public commitment, and on any milestone or terminal wake for a Relay-linked task before posting its completion follow-up; relevant only when Relay is on.
- `firstmate-codexapp` - load before coordinating a visible Codex Desktop thread, evaluating a Codex App backend request, or reconciling Codex Desktop host-tool smoke evidence for Firstmate work.
- `firstmate-coding-guidelines` - load before changing firstmate's shared, tracked material, as defined by section 1's list, whether editing directly or briefing a crewmate for a firstmate-repo task.
- `human-text-discipline` - load before writing or editing a pull request body, a commit message, or a captain-facing chat message; it owns the checkable AI-tell list for text a human reads and does not restate section 9.
2 changes: 1 addition & 1 deletion .agents/skills/bootstrap-diagnostics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ When any diagnostic needs captain attention, report the plain consequence and re
Neither copy is a safe winner: union-merge them into this home's file by task id, resolve each conflicting id to its most recent real transition, check this home's archive before treating a missing Done row as lost, and verify the merged id set equals the union of both inputs before installing it.
Then move the code-root file aside rather than deleting it, tell the captain which rows were recovered, and run every later backlog command through `bin/fm-tasks-axi.sh`; re-linking the code-root copy is never the fix, because the next cwd-relative tasks-axi write replaces the link again.
- `SECONDMATE_SYNC: secondmate <id>: skipped: <reason>` - secondmate convergence left a live home on its existing checkout because the home was dirty, diverged, unsafe, on the wrong branch, missing its placement-specific target commit, unreachable, or otherwise not fast-forwardable, or because inherited local-material propagation failed; bootstrap continued, but inspect the reason because the secondmate's tracked instructions, inherited settings, or shared captain preferences may be stale after a primary update.
- `SECONDMATE_LIVENESS: secondmate <id>: skipped: <reason>|respawn failed after <cause>: <reason>` - the session-start liveness sweep could not guarantee that the registered secondmate is running a real agent process.
- `SECONDMATE_LIVENESS: secondmate <id>: skipped: <reason>|respawn failed after <cause>: <reason>|gap: <reason>` - the session-start liveness sweep could not guarantee that the registered secondmate is running a real agent process; a `gap:` line means a registered secondmate had no usable task record and relaunching it from the registry also failed.
Investigate the reason because that secondmate is not guaranteed live.
- `SECONDMATE_HANDOFF: secondmate <id>: pending delivery: <n> item(s)` - queued work has already left the main dispatchable backlog and remains safe in the named remote route's backlog-format outbox because backlog receipt or local outbox cleanup has not completed; [`bin/fm-backlog-handoff.sh`](../../../bin/fm-backlog-handoff.sh) owns the release contract.
Preserve that outbox and rerun `bin/fm-backlog-handoff.sh --resume-pending` after the route, receipt, or cleanup problem is resolved; never re-add or dispatch the items from the main backlog.
Expand Down
8 changes: 5 additions & 3 deletions .agents/skills/harness-adapters/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: harness-adapters
description: >-
Agent-only reference for firstmate harness operations.
Use before spawning or recovering a crewmate or secondmate, handling a trust dialog, sending a harness-specific skill invocation, interrupting or exiting an agent, resuming an exited agent, or verifying a new harness adapter.
Contains verified facts for claude, codex, opencode, pi, pi-signed, grok, kimi, cursor, gemini, muse, rovo, omp, agy, and devin.
Contains verified facts for claude, codex, opencode, pi, pi-signed, grok, kimi, cursor, gemini, muse, rovo, omp, agy, devin, cline, and openhands.
user-invocable: false
metadata:
internal: true
Expand Down Expand Up @@ -35,7 +35,7 @@ For recovery and control, use the exact `harness=` in `state/<id>.meta`; never i
Deliver lifecycle actions only through `../../../bin/fm-control.sh <task-id> interrupt|exit|relaunch`.
Never type an interrupt key or exit command through `fm-send`, where routing-marked lifecycle text becomes chat.
Trust handling is complete only when inspection proves the target started processing its instructions; delivery success alone is not proof.
Muse, Gemini, AGY, and Devin are verified only for crewmate and scout work, never a secondmate or primary.
Muse, Gemini, AGY, Devin, Cline, and OpenHands are verified only for crewmate and scout work, never a secondmate or primary.

## Detection

Expand Down Expand Up @@ -96,7 +96,9 @@ A new tool remains undispatchable until the `verify` plan, its harness entry, ev
"rovo": "references/harness/rovo.md",
"omp": "references/harness/omp.md",
"agy": "references/harness/agy.md",
"devin": "references/harness/devin.md"
"devin": "references/harness/devin.md",
"cline": "references/harness/cline.md",
"openhands": "references/harness/openhands.md"
}
}
```
2 changes: 1 addition & 1 deletion .agents/skills/harness-adapters/references/harness/agy.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Verified as a CREWMATE and SCOUT adapter only; `../../../../../bin/fm-spawn.sh`
| Resume | `--continue` and `--conversation` exist but carry no verified pane-resume contract; use deterministic relaunch. |
| Model | `--model <id>` with the bare catalog id from `agy models` (for example `gemini-3.8-flash-high`); `bin/fm-spawn.sh` refuses a requested id a reachable listing omits. The listing is a remote fetch, so the probe runs stdin-detached under the shared hard bound and an unreachable or hung listing launches unvalidated with a notice. |
| Effort | `--effort low\|medium\|high`; `xhigh` and `max` stay in task metadata under the record-and-omit contract. |
| Composer | Borderless bare `>` row, which the shared classifier reads as `unknown` under the dead-shell rule, never `empty`; steering confirms delivery through native agent-state and the delivery footer instead, the cursor precedent. |
| Composer | Borderless bare `>` row pinned above a full-width `─` rule. The shared classifier reads it `empty` only with a live agy identity (tmux foreground process, herdr `agent get`), and `unknown`/`pending` otherwise, so the dead-shell rule still guards every other pane; `bin/fm-control.sh exit` needs that identity proof to type `/quit`. Steering still confirms delivery through native agent-state and the delivery footer. |

## Trust, and where the decision persists

Expand Down
14 changes: 14 additions & 0 deletions .agents/skills/harness-adapters/references/harness/claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Busy hooks verified 2026-07-28 on Claude Code 2.1.220.
| Model | `--model <model>`; discover through the interactive `/model` picker, with alias or full-name shape documented by `claude --help`. |
| Effort | `--effort <low\|medium\|high\|xhigh\|max>`, verified on 2.1.196. |
| Permissions | `--dangerously-skip-permissions` by default, or `--permission-mode auto` when `config/claude-permission-mode` is `auto`; the `auto` shape verified on 2.1.269. See [`Claude permission mode`](../../../../../docs/configuration.md#claude-permission-mode-configclaude-permission-mode) for the launch grant and configuration. |
| Config seat | `--claude-config-dir <dir>` on `fm-spawn.sh` picks the `CLAUDE_CONFIG_DIR` this one claude spawn's pane resolves into, validated and recorded in the task's own meta and reused whenever that same task spawns again, so two claude lanes can sit on different accounts at once; `fm-spawn.sh --help` owns the exact contract. |

## Workspace trust

Expand All @@ -29,6 +30,8 @@ When the project entry instead already carries an explicit decline (`hasClaudeMd
Both flags `false` is Claude Code's default entry for a project never asked, not a decline, and is treated like an absent flag: trust registers and the import dialog still renders.
The why-two-entries mechanism and the consent-gating logic live in the script's own header comment, which is the one owner for that contract; the fact worth repeating here is that `../../../bin/fm-spawn.sh` refuses the spawn when the trust flag fails to land, rather than launching a worker that would wedge on that dialog.

A seated spawn (`--claude-config-dir`, see "Config seat" above) passes that same directory as `CLAUDE_CONFIG_DIR` to this registration call, so trust always lands in the exact store the launched process itself reads - never firstmate's own ambient store.

Never try to answer either dialog with a key.
Firstmate's key plane carries only Enter, Escape, and C-c with no arrow navigation, so it cannot move a dialog's selection at all, and both dialogs render with the cursor on their declining option, which means a sent Enter ends the session instead of accepting.
A visible trust dialog means pre-registration did not take effect (or the project entry already carries an explicit decline) - inspect the store and the spawn's error output rather than sending keys.
Expand All @@ -43,8 +46,19 @@ Never send Enter to that one either: it was observed rendering in the same shape
Firstmate cannot move a selection with Enter, Escape, and C-c alone, so it cannot accept this dialog at all, and an operator accepts it once per machine instead.
Inspect the pane to identify which dialog is on screen, and report it rather than answering it.
A launch under `config/claude-permission-mode=auto` never meets the bypass confirmation, because it does not request bypass mode: on 2.1.269 `claude --permission-mode auto` reached the composer directly with the footer `⏵⏵ auto mode on (shift+tab to cycle)`, so a captain who refuses the bypass dialog selects `auto` there instead of accepting it.
That setting is fleet-wide rather than per seat (the Permissions row above names its owner), so choosing `auto` to avoid the dialog for one seat moves every claude lane in that home with it.
The workspace-trust dialog is unaffected by the permission mode and still needs the pre-registration above.

### Preparing a config seat

Preparing a seat for `--claude-config-dir` (see "Config seat" above) is two interactive steps, not one, and the operator does both by hand before the first seated spawn.
First, log the account in for that store: `CLAUDE_CONFIG_DIR=<dir> claude`.
Second, accept that store's own bypass-permissions confirmation, which the login alone never raises - only `--dangerously-skip-permissions` asks for it - so the one command that reaches both in a single sitting is `CLAUDE_CONFIG_DIR=<dir> claude --dangerously-skip-permissions`, accepting whatever it shows.
A seat that was only logged into is the trap: it holds a `.claude.json`, so `fm-spawn.sh`'s seat check accepts it, and the pane then wedges on the bypass dialog that firstmate cannot answer, which the supervisor reads as a stuck agent.
`../../../../../docs/verification/runtime-backends.md` records that exact counter-case: an isolated `CLAUDE_CONFIG_DIR` holding only a copied `.claude.json` cleared the trust dialog and then surfaced the bypass warning.
A captain who will not accept that dialog for a seat runs the whole home under `config/claude-permission-mode=auto` instead, with the fleet-wide consequence stated above; there is no per-seat way to decline it.
`fm-spawn.sh` cannot verify either step: no record of the bypass acceptance was found in `.claude.json` on 2.1.278 (key names only, top level and `projects.<path>`, where `hasTrustDialogAccepted` and the import-consent flags do live), and whether Claude Code records it elsewhere was not checked, so the seat check confirms only that a store exists at that path.

## Composer ghost

Completed turns can render dim predicted text inside an empty composer, indistinguishable in plain `tmux capture-pane`.
Expand Down
55 changes: 55 additions & 0 deletions .agents/skills/harness-adapters/references/harness/cline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Cline CLI

Cline's `cline` TUI, verified end to end on 2026-09-16 with cline 3.0.62 on Linux through the tmux and Herdr backends.
Verified as a CREWMATE and SCOUT adapter only; `../../../../../bin/fm-spawn.sh` refuses a secondmate launch on it because `../../../../../docs/supervision-protocols/` carries no cline wake protocol.
`../../../../../docs/verification/cline.md` owns how every fact below was established and what is still unproven.

## Operating facts

| Fact | Value |
|---|---|
| Binary | `cline` from `PATH`, refused if absent. The installed launcher is a Node wrapper that spawns the long-lived agent as a native binary whose live process name is exactly `.cline` (verified: `ps -o comm=` reports `.cline` with `argv[0]` `.cline`). |
| Launch | `cline -i -c <worktree> --auto-approve true -m <provider>/<model> --thinking <level>`, launched BARE and given its brief only after the readiness gate below. `--model` takes the full `<provider>/<model>` id (`cline-pass/deepseek-v4-flash`); cline derives the provider from the prefix, so no separate `-P` is passed. |
| Brief delivery | Launch-then-send, the kimi/rovo shape, because cline's one-time "Introducing Cline Desktop" first-run splash consumes the first submitted line; the gate dismisses the splash with Escape, waits for cline's `Auto-approve` status row, then types the absolute brief pointer once and confirms delivery from the recorded `busy cline-hook` state (never by re-driving Enter). |
| Busy state | Workspace hook config files under `.cline/hooks` (source `cline-hook` in `../../../../../bin/fm-busy-lib.sh`): `TaskStart` opens a turn; `TaskComplete`, `TaskCancel`, `TaskError`, and `SessionShutdown` all close it. `../../../../../bin/fm-spawn.sh` arms the busy generation, writes the files before launch, and excludes `.cline/` from git's view. |
| Rendered tail | The in-transcript busy row reads `⠸ Thinking... (esc to cancel)`; when the turn ends the same row is rewritten as `▶ Thinking:` with the token gone. The bottom status row (`⏵⏵ Auto-approve all enabled (Shift+Tab)`) does NOT change between busy and idle, so it is not a signal. |
| Turn end | The `TaskComplete` hook touches `state/<id>.turn-ended` (the watcher NOTIFICATION) in addition to closing the busy record. |
| Exit | `/exit`, one Enter; the process exits. |
| Interrupt | Single `Escape`, which stops the running turn and leaves the composer at its `Ask anything...` placeholder with no prompt repollution, so no clear key follows. |
| Skill | No verified slash-skill form for injected instructions; use natural language. |
| Autonomy | `--auto-approve true` (cline's documented default, passed explicitly) auto-approves every tool call for the run. |
| Marker | None; a live TUI carries no cline-identity variable. Detected by ancestry alone. |
| Resume | `--id <session-id>` resumes an existing session and `cline history --json` lists session ids, but no verified pane-resume contract exists; use deterministic relaunch. |
| Model | `-m <provider>/<model>`; a value without `/` is refused by cline with `invalid model format. Expected format: modelType/model`. `bin/fm-spawn.sh` passes the id through unchanged. |
| Effort | `--thinking none\|low\|medium\|high\|xhigh`; `max` stays in task metadata under the record-and-omit contract. |
| Composer | A bordered composer with the bare agent glyph `❯` and a muted-truecolor idle placeholder (`Ask anything...`, or the fresh-session `What can I do for you?`). Both placeholders are in `../../../../../bin/fm-composer-lib.sh`'s fleet-wide idle set, but cline renders them at ~135.5 perceived luminance, just above the shared ghost-luma ceiling of 128, so on the styled tmux/herdr captures an idle cline composer classifies `pending`, never `empty`. This is the same known gap `../../verification/rovo.md` documents; cline readiness/delivery therefore lead with the `Auto-approve` status row and the recorded busy hook, and cline steering relies on the shared queued-Enter busy conversion. |

## Trust, dialogs, and the first-run splash

Cline was not observed to gate a fresh worktree behind a folder-trust dialog, and no launch flag or trust store was needed; `--auto-approve true` covers tool approval.
The one first-run obstacle is the "Introducing Cline Desktop" splash, which renders only until it is dismissed once per profile and, if present, swallows the first submitted line.
The readiness gate (`cline_wait_for_ready` in `../../../../../bin/fm-spawn.sh`) detects the splash text and sends one Escape before polling for the idle composer, so a fresh-profile worker cannot lose its brief.

## Credential precondition

A verified cline worker ran under a signed-in ClinePass subscription with no key export.
Authorize with `cline auth -p cline-pass` (or the positional `cline auth cline-pass`) on a TTY; the credential lands in `~/.cline/data/settings/providers.json`.
The unauthenticated failure mode was not observed, so treat any auth prompt or refusal as a credential blocker under `../../../../../AGENTS.md` section 9, fix the environment, and retire the endpoint rather than typing into it.

## Detection

Detected by ancestry alone: `../../../../../bin/fm-harness.sh` matches the anchored process name `.cline` (and, as a backup for the node wrapper, the anchored script-path fragments `/bin/cline` and `@cline/cli`).
No environment marker is promoted: cline publishes none, and it does not clear an inherited `CLAUDECODE`, so `../../../../../bin/fm-spawn.sh` clears the foreign primary markers at the launch boundary for the same reason cursor and muse do.
cline is deliberately absent from the session-lock name vocabulary in `../../../../../bin/fm-session-lock-lib.sh`, where muse, gemini, rovo, and agy are also absent: a crewmate-only adapter must never own a home session lock.

## Worker busy state and turn end

`../../../../../bin/fm-spawn.sh` arms the busy generation with a seed record of `idle/fm-spawn` (the bare launch is not a submitted turn), then writes the five `.cline/hooks` files before launch.
`TaskStart` writes `busy cline-hook` when the brief pointer is submitted, so spawn delivery confirmation reads the same recorded state the supervisor later reads; the `(esc to cancel)` rendered token is only a fallback for a pane whose hook has not landed.
Teardown and relaunch retire the hook files through `fm_control_harness_wiring_paths` in `../../../../../bin/fm-control-lib.sh`.

## Primary integration

Unsupported and unverified.
`../../../../../docs/supervision-protocols/` carries no cline protocol, no turn-end guard adapter exists for it, and this adapter verified only the crewmate-side launch, busy state, interrupt, and exit.
`references/common/primary-hooks.md`'s unsupported-boundary rule applies: never invent a wake protocol from a similar TUI.
Loading