Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 19 additions & 3 deletions .agents/skills/harness-adapters/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
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, and grok.
description: >-
Agent-only reference for firstmate harness operations.
Use before spawning or recovering a crewmate or secondmate, handling a trust or permission 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, and grok.
user-invocable: false
metadata:
internal: true
Expand Down Expand Up @@ -47,6 +50,14 @@ When verifying a new adapter, record its env marker and command name in `bin/fm-
For stuck recovery, the target window's harness is recorded as `harness=` in `state/<id>.meta`.
Use that value for interrupt, exit, resume, and skill-invocation facts.

## Mid-run permission prompts

Firstmate launches every verified crewmate harness in its unattended mode, but a managed policy, a newly introduced tool class, or a failed autonomy flag can still produce a permission prompt after work has started.
`bin/fm-watch.sh` owns the deterministic pane matcher and the busy/no-progress system-dialog fallback.
`docs/permission-stall-detection.md` owns the empirical capture and macOS capability record.
On either permission wake, load `stuck-crewmate-recovery` and follow its permission-blocked branch before using any interrupt, exit, or relaunch mechanic below.
Never apply an adapter's startup trust-dialog acceptance rule to a mid-run command or capability grant.

## Primary turn-end guard

Every verified primary harness has an empirically validated hook path for the "no turn ends blind" guard.
Expand Down Expand Up @@ -109,10 +120,13 @@ Natural language is acceptable if uncertain.
| Exit command | `/exit` |
| Interrupt | single Escape |
| Skill invocation | `/<skill>` (e.g. `/no-mistakes`) |
| Protected mid-run grant shapes | `Do you want to proceed?` together with the `Esc to cancel · Tab to amend` footer, or `Quick safety check: Is this a project you created or one you trust?` together with `Yes, I trust this folder`, `No, exit`, and the confirmation footer. |

First launch in a fresh worktree, or first ever on a machine, may show a trust or bypass-permissions confirmation.
After every spawn, peek the pane within about 20 seconds.
If such a dialog is showing, accept it from an active firstmate session using `FM_HOME=<this-firstmate-home> bin/fm-send.sh <window> --key Enter`, or the choice the dialog requires, unless `FM_HOME` is already set to the active firstmate home; verify the brief started processing.
If a workspace trust or bypass-permissions dialog is showing before the brief starts processing, accept it from an active firstmate session using `FM_HOME=<this-firstmate-home> bin/fm-send.sh <window> --key Enter`, or the choice the dialog requires, unless `FM_HOME` is already set to the active firstmate home; verify the brief started processing.
The separate `Hooks need review` prompt with `Trust all on first launch` is Firstmate's own supervision-hook trust path and must also be accepted with Enter before the brief starts.
Once the brief has started processing, treat either protected shape in the table as a security-sensitive mid-run grant and escalate it to the captain without pressing an approval or denial key.

Claude renders a predicted-next-prompt suggestion as dim/faint text inside an otherwise-empty composer after a turn completes.
A plain `tmux capture-pane` cannot tell that ghost text apart from typed text.
Expand All @@ -139,6 +153,7 @@ Claude Code's primary watcher protocol is the lowest-friction path: run `bin/fm-
| Exit command | `/quit` (slash popup needs about 1 second between text and Enter; `fm-send` handles it) |
| Interrupt | single Escape |
| Skill invocation | `$<skill>` (e.g. `$no-mistakes`); `/<skill>` is claude-only and codex rejects it as "Unrecognized command" |
| Protected mid-run grant shapes | One of `Would you like to run the following command?`, `Would you like to grant these permissions?`, `Would you like to make the following edits?`, or `Do you want to approve network access to "<host>"?`, together with its title-specific choices and `Press enter to confirm or esc to cancel`; or `Do you trust the contents of this directory?` together with `Yes, continue` and `No, quit`. |

A `$<skill>` invocation opens a `$`-autocomplete (skill) popup, the same hazard as the `/` slash popup: submitting too fast lets the popup swallow the Enter, so the invocation never lands.
`fm-send` handles it the same way it handles `/` - it gives the popup a longer settle (1.2s) between typing and the first Enter, with the target backend's submit retry as the safety net - but the `$` settle is scoped to `harness=codex`, read from the target metadata for exact task ids or legacy `fm-<id>` labels.
Expand All @@ -147,8 +162,9 @@ An explicit `session:window` target has no meta, so its harness is unknown and t
This is why the validation trigger (`$no-mistakes`) to a codex crew now lands on the first Enter instead of biting the popup.

Directory trust dialog on first run per repo root: "Do you trust the contents of this directory?"
Accept with Enter.
Accept with Enter only during the spawn-time peek before the brief starts processing.
The decision persists for the repo, so later worktrees of the same project skip it.
If that shape appears after work has started, escalate it to the captain as a mid-run directory-trust grant without pressing an approval or denial key.

Resume after exit with `codex resume <session-id>`.
The session id is printed on quit.
Expand Down
29 changes: 25 additions & 4 deletions .agents/skills/stuck-crewmate-recovery/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
---
name: stuck-crewmate-recovery
description: Agent-only playbook for stuck firstmate direct reports. Use after a stale wake, looping pane, repeated confusion, an answered-by-brief question, an unresponsive crewmate, or a failed steer. Escalates from peek, to one-line steer, to harness-specific interrupt, to relaunch with progress, to failed status.
description: >-
Agent-only playbook for stuck firstmate direct reports.
Use after a stale wake, permission-prompt or system-dialog suspicion, looping pane, repeated confusion, an answered-by-brief question, an unresponsive crewmate, or a failed steer.
Routes permission grants to the captain before the ordinary peek, steer, interrupt, relaunch, and failed-status ladder.
user-invocable: false
metadata:
internal: true
---

# stuck-crewmate-recovery

Use this playbook when a direct report is stale, looping, repeatedly confused, asking a question its brief already answers, unresponsive, or when a steer failed to land.
Use this playbook when a direct report is stale, permission-blocked, looping, repeatedly confused, asking a question its brief already answers, unresponsive, or when a steer failed to land.

Load `harness-adapters` before sending an interrupt, exit command, resume command, or harness-specific skill invocation.
Load `harness-adapters` before handling a permission prompt or sending an interrupt, exit command, resume command, or harness-specific skill invocation.
The target window's harness is recorded as `harness=` in `state/<id>.meta`.

Escalate in order:
## Permission-blocked branch

Handle permission evidence before the ordinary recovery ladder because approving, denying, interrupting, or relaunching can change the security decision or hide the only useful evidence.

1. Peek the pane and read the requested command, tool, directory, or capability.
2. If the wake says `permission-prompt detected`, or the pane matches the target's mid-run permission shape in `harness-adapters`, do not press an approval or denial key and do not interrupt or relaunch the agent.
3. Tell the captain what action is waiting and ask them to approve or deny it in the visible prompt.
Keep the message product-facing, such as `Work is blocked waiting for permission to <action>; please approve or deny that request in the visible dialog.`
Do not name the harness, pane, task id, watcher, or other fleet machinery.
4. If the wake says `permission/system-dialog suspected`, remember that it is a timeout heuristic rather than a directly observed macOS dialog.
Inspect the pane for the foreground action and any clear evidence of ordinary progress.
If ordinary progress is visible, resume supervision and treat the wake as a conservative false positive.
Otherwise tell the captain `A macOS permission dialog may be blocking <action>; please grant or deny it in the visible dialog or in Privacy & Security in System Settings.`
5. Never try to grant a macOS TCC permission with keystrokes, AppleScript, Accessibility APIs, `tccutil`, or a settings-database edit.
macOS owns that decision and the captain must make it.
6. After the captain clears the dialog, verify that the pane changes or `bin/fm-crew-state.sh <id>` reports resumed work before returning to supervision.
If the permission was denied and the task still cannot proceed, use the ordinary ladder below with that denial as evidence.

For a non-permission stall, escalate in order:

1. Peek the pane.
2. If the crewmate is waiting on a question its brief already answers, answer in one line via `FM_HOME=<this-firstmate-home> bin/fm-send.sh` from an active firstmate session unless `FM_HOME` is already set to the active firstmate home.
Expand Down
18 changes: 10 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ state/ volatile runtime signals; gitignored
.afk durable away-mode flag; present = sub-supervisor may inject escalations (set by /afk, cleared on user return)
.lock per-home session lock written by fm-lock.sh: harness PID on line 1, holder process start time on line 2; direct readers parse only the first-line PID, while acquire/status treat it as held only when that PID is live with a matching start time (skipped for a legacy one-line lock, which is held while its PID is a live harness) and is not a Codex app-server
.watch.lock .wake-queue.lock watcher singleton and queue serialization locks
.hash-* .count-* .stale-* .stale-since-* .paused-* .wedge-escalations-* .seen-* .hb-surfaced-* .last-* .heartbeat-streak watcher internals; never touch
.hash-* .count-* .stale-* .stale-since-* .paused-* .wedge-escalations-* .brief-started-* .seen-* .hb-surfaced-* .last-* .heartbeat-streak watcher internals; never touch
.watch-triage.log watcher's absorbed-wake debug log (size-capped); never relied on, safe to delete
.last-watcher-beat watcher liveness beacon, touched every poll (including while absorbing benign wakes); guard scripts read it
.subsuper-* .supervise-daemon.* sub-supervisor internals; never touch
Expand Down Expand Up @@ -561,11 +561,11 @@ Whenever at least one task is in flight, keep exactly one live supervision wait
The emitted block is the only per-harness operating recipe in the session context.
Do not substitute another harness's command shape for it.
**Always-on wake triage (absorb only when provably working).**
`bin/fm-watch.sh` classifies every wake in bash and absorbs the benign majority without waking you: crews with positive working evidence (an actively-running no-mistakes step for their branch, or a busy pane, read via `bin/fm-crew-state.sh`), a declared `paused:` external wait until its bounded recheck cadence, and no-change heartbeats.
`bin/fm-watch.sh` classifies every wake in bash and absorbs the benign majority without waking you: crews with positive working evidence (an actively-running no-mistakes step for their branch, or a busy pane read via `bin/fm-crew-state.sh`) unless the separate permission-stall no-progress threshold has expired, a declared `paused:` external wait until its bounded recheck cadence, and no-change heartbeats.
It never absorbs a crewmate that stopped without that evidence - whatever its stale status log claims - and only an actionable wake is queued durably and ends the supervision wait, so you resume the emitted protocol exactly once per actionable event.
A `paused:` status is a deliberate external wait, not `blocked:`; its initial signal still surfaces once, and a forgotten pause re-surfaces for a recheck once per window.
Repeated provably-working stale escalations on one unchanged pane eventually add `demand-deep-inspection` to the wake reason so it is not mistaken for another routine validation wait.
`docs/architecture.md` ("Event-driven supervision") owns the full classification mechanism, its thresholds, and the shared classifier library; while `state/.afk` exists the daemon owns triage and the watcher surfaces every wake to it.
Repeated unchanged wedge or permission-stall escalations eventually add `demand-deep-inspection` to the wake reason so they are not mistaken for another routine validation wait.
`docs/architecture.md` ("Event-driven supervision") owns the general classification mechanism and shared classifier library, while `docs/permission-stall-detection.md` owns permission-prompt matching and the macOS timeout heuristic; while `state/.afk` exists the daemon owns triage and the watcher surfaces every wake to it.
At the start of every wake-handling turn, run `bin/fm-wake-drain.sh` before peeking panes, reading status files beyond the reason line, or starting new work.
Session-start recovery is the exception: `bin/fm-session-start.sh` already drained the queue when locked, or deliberately skipped the drain when read-only because another session owns it.
The printed reason line is still useful, but the drained queue is the lossless backlog.
Expand Down Expand Up @@ -600,7 +600,9 @@ On wake, in order of cheapness:
1. Read the reason line and drain queued wake records with `bin/fm-wake-drain.sh`.
2. `signal:` read the listed status files first; a wake lists every signal that landed within the coalescing grace window (e.g. a status write plus the same turn's turn-end marker), and each is ~30 tokens and usually sufficient.
A status line is the wake *event*, not the crewmate's current state; when you need the live state - especially to confirm a `needs-decision`/`blocked`/`paused` status is still real and not already resolved-and-resumed - read it with `bin/fm-crew-state.sh <id>`, which reconciles the authoritative run-step over the possibly-stale log line, and never `tail` the status log as the current-state source.
3. `stale:` the crewmate stopped without reporting; peek the pane (`bin/fm-peek.sh <window>`) to diagnose.
3. `stale:` the crewmate stopped without reporting, a recognized mid-run permission prompt is waiting, or a busy pane exceeded the possible system-dialog no-progress threshold.
If the reason includes `permission-prompt detected` or `permission/system-dialog suspected`, load `stuck-crewmate-recovery` before taking any ordinary recovery action and follow its permission-blocked branch.
Otherwise peek the pane (`bin/fm-peek.sh <window>`) to diagnose.
If the stale reason includes `demand-deep-inspection`, inspect the pane, `bin/fm-crew-state.sh <id>`, and the validation logs before resuming supervision.
If the pane is waiting, looping, confused, or unresponsive, load `stuck-crewmate-recovery`.
4. `check:` a per-task poll fired (usually a merge, or X mode when enabled); act on it.
Expand Down Expand Up @@ -655,7 +657,7 @@ Inline facts that must survive without a loaded skill:

### Stuck-crewmate recovery

On `stale`, looping, repeated confusion, an answered-by-brief question, an unresponsive pane, or a failed steer, load `stuck-crewmate-recovery`.
On `stale`, `permission-prompt detected`, `permission/system-dialog suspected`, looping, repeated confusion, an answered-by-brief question, an unresponsive pane, or a failed steer, load `stuck-crewmate-recovery`.
That playbook escalates from peek, to one-line steer, to harness-specific interrupt, to relaunch with a progress note, to `failed` with evidence.

## 9. Escalation and captain etiquette
Expand Down Expand Up @@ -774,10 +776,10 @@ It performs only fast-forward self-updates of firstmate and registered secondmat
These skills are not captain-invocable; they are conditional operating references you must load at the trigger points below.

- `bootstrap-diagnostics` - load whenever the session-start digest's bootstrap section prints any diagnostic or capability line (`MISSING:`, `MISSING_MANUAL:`, `BACKEND_INVALID:`, `ACCOUNT_ROUTING:`, `NEEDS_GH_AUTH`, `TANGLE:`, `CREW_HARNESS_OVERRIDE:`, `CREW_DISPATCH:`, `FLEET_SYNC:`, `SECONDMATE_SYNC:`, `SECONDMATE_LIVENESS:`, `TASKS_AXI:`, `NUDGE_SECONDMATES:`, `REPORT_RETENTION:`, or `FMX:`); silence needs no load.
- `harness-adapters` - load 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.
- `harness-adapters` - load before spawning or recovering a crewmate or secondmate, handling a trust or permission dialog, sending a harness-specific skill invocation, interrupting or exiting an agent, resuming an exited agent, or verifying a new harness adapter.
- `operating-fundamentals` - load when intaking any captain ask, deciding whether to dispatch or work inline, supervising under load, handling a blocked lane or finished crew, protecting shared validation capacity, acting on an explicit captain order, or about to assert a fleet fact.
- `firstmate-orca` - load before recovering or supervising legacy Orca-backed work, testing Orca backend behavior, debugging Orca task state, or reconciling Orca-backed task metadata.
- `stuck-crewmate-recovery` - load after a stale wake, looping pane, repeated confusion, an answered-by-brief question, an unresponsive crewmate, or a failed steer.
- `stuck-crewmate-recovery` - load after a stale wake, permission-prompt or system-dialog suspicion, looping pane, repeated confusion, an answered-by-brief question, an unresponsive crewmate, or a failed steer.
- `secondmate-provisioning` - load before creating, seeding, validating, launching, handing backlog to, recovering, pushing inherited config into, or retiring a secondmate home, and before editing `data/secondmates.md`.
- `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 X-mode configuration blocker, and on any milestone or terminal wake for an X-mode-linked task before posting its completion follow-up; relevant only when X mode 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.
Expand Down
Loading