Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b42d4fa
fix(bin): report a Lavish source armed only after its listener is run…
tiago-peixoto Sep 24, 2026
52e679b
fix(bin): stop repeating unknown-wake escalations that were already d…
tiago-peixoto Sep 25, 2026
c6e816f
fix(bin): keep a stated default-key retraction from cancelling a keyl…
tiago-peixoto Sep 25, 2026
a8572f6
fix(bin): terminate a remote job worker that lost ownership on TERM (…
tiago-peixoto Sep 25, 2026
4be8a40
docs: make configuration settings easier to find and understand (#5589)
tmchow Sep 25, 2026
b52d401
fix: limit project memory edits to factual corrections (#5636)
kunchenguid Sep 25, 2026
ca8c293
feat: permit gate lifecycle calls against disposable lab homes (#5635)
kunchenguid Sep 25, 2026
b575497
fix(bin): evict a watcher whose beacon stalls past a hard bound inste…
karotkriss Sep 25, 2026
5cec4e2
fix(pi): hide queued Firstmate inputs under Calm only when the sessio…
tiago-peixoto Sep 25, 2026
756f64e
fix(bin): refuse teardown when a required source disappears (#5548)
tiago-peixoto Sep 25, 2026
4e99de7
docs: make supervision-host easier to read (#5605)
tmchow Sep 25, 2026
660fa4a
docs: make the Herdr backend guide easier to read (#5606)
tmchow Sep 25, 2026
5323582
docs: make pi-supervision-branch easier to read (#5607)
tmchow Sep 25, 2026
d18a220
docs: make watcher-continuity easier to read (#5608)
tmchow Sep 25, 2026
70e41a8
docs: make sessionstart-nudge easier to read (#5609)
tmchow Sep 25, 2026
7c501a1
docs: make captain-hold-lifecycle easier to read (#5610)
tmchow Sep 25, 2026
c60f0ab
docs: make remote-secondmates easier to read (#5612)
tmchow Sep 25, 2026
683b3eb
fix(bin): bound the away digest and log why a delivery failed (#5554)
Sophylax Sep 25, 2026
dbe124d
test: add a gated harness seam and stabilize lifecycle fixtures (#5638)
kunchenguid Sep 25, 2026
1a814e4
fix(bin): refuse watchers from disposable checkouts and exit when the…
karotkriss Sep 25, 2026
84362f6
fix(bin): surface unrecognized status prefixes instead of reading the…
tiago-peixoto Sep 25, 2026
e97390a
fix(bin): report the failing item when remote inheritance fails (#5658)
karotkriss Sep 25, 2026
c643b57
fix(bin): stand down the Claude Stop auto-arm on pi-code-delivered pa…
karotkriss Sep 25, 2026
d1abcd6
fix(bin): match whole multi-word project names in the registry lookup…
karotkriss Sep 25, 2026
b805823
fix(bin): classify shell stdin payloads after -s operands (#5546)
coreldh Sep 25, 2026
83a4bbd
fix(bin): strip AI co-author trailers from fleet-launched commits (#5…
tiago-peixoto Sep 25, 2026
67f6c27
fix(bin): treat Pi's dollar-first cost footer as furniture (#5683)
tiago-peixoto Sep 25, 2026
8d2ee29
fix(bin): refuse merges with unreported required checks (#5534)
mremond Sep 25, 2026
0154324
fix: keep persistent secondmates out of landed-work cleanup (#5696)
kunchenguid Sep 25, 2026
f54aa00
fix: reject invalid X reply and follow-up arguments before posting (#…
kunchenguid Sep 25, 2026
3c14a54
fix: pause broken supervision-host sessions between engine probes (#5…
kunchenguid Sep 25, 2026
97365aa
fix(bin): absorb routine secondmate working and paused status appends…
karotkriss Sep 25, 2026
c4c9d63
fix(bin): use gh-axi for the ship DoD draft check (#5519)
karotkriss Sep 25, 2026
f1ea9ed
fix(bin): bind inactive-outcome receipt identity to structured fields…
karotkriss Sep 25, 2026
4299683
feat: record the Claude and Cursor dialog for the supervision host (#…
kunchenguid Sep 25, 2026
c33b3f6
fix(bin): retire check-row receipts on branch acknowledgement so away…
kunchenguid Sep 26, 2026
1fe1a67
fix(bin): deliver Claude-bound operational input as a record-backed d…
kunchenguid Sep 26, 2026
ef595d8
test: isolate lint fixture from tracked suite (#5727)
kunchenguid Sep 26, 2026
e789e52
fix(bin): republish parent metadata after a remote secondmate relaunc…
tiago-peixoto Sep 26, 2026
9a4cfbb
fix(bin): give slow watcher suites headroom under the changed-suite b…
karotkriss Sep 26, 2026
df4ae5d
fix(bin): stop nested steal-lock recursion and mid-steal watcher TERM…
kunchenguid Sep 26, 2026
873c923
test: make supervision-host park-boundary tests deterministic (#5710)
kunchenguid Sep 26, 2026
ea7c7f7
fix: stage remote home clones before publication (#5733)
kunchenguid Sep 26, 2026
920a7d9
fix: preserve Herdr status on Pi relaunch (#5161)
sdivanl Sep 26, 2026
65c53fb
Seed the relaunch-ordering PR poll fixture without fm-pr-check.sh (#5…
kunchenguid Sep 26, 2026
9b52cf5
feat: add attended supervision for Claude and Cursor hosts (#5748)
kunchenguid Sep 26, 2026
72b63ee
fix(bin): dedup directed source expansions in fm-pending-reply-lib (#…
kunchenguid Sep 26, 2026
8430bdb
Merge upstream/main through 72b63eee
ashkonmousavi Sep 26, 2026
0cb7f36
no-mistakes(review): Remove duplicated lavish answers paragraph in ho…
ashkonmousavi Sep 26, 2026
1c58d35
no-mistakes(document): Repair merge-damaged herdr and sessionstart-nu…
ashkonmousavi Sep 26, 2026
188dba5
no-mistakes(review): Restore upstream Pi Calm extension test byte-for…
ashkonmousavi Sep 26, 2026
fec95ee
no-mistakes(review): Restore fork doc facts lost in merge conflict re…
ashkonmousavi Sep 26, 2026
cf7d540
no-mistakes(ci): Root cause: "Behavior portable serial 8" failed on o…
ashkonmousavi Sep 26, 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
43 changes: 26 additions & 17 deletions .agents/skills/afk/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Hold-for-return is the default and the only reach profile this release records:

No `/back` is needed. The first genuine message is the return signal:

- A message **without** the current operational prefix or a legacy bare marker, and **not** starting with `/afk` -> the captain is back.
- A message that is none of the internal forms below, and **not** starting with `/afk` -> the captain is back.
Run `bin/fm-afk-return.sh` before acting on the message that brought the captain back.
That script owns the correct-ordered daemon shutdown where a daemon ran, the archive of the posture record, durable wake presentation and post-handling acknowledgement, escalation and wedge evidence, the return brief, and the return-catch-up gate.
Relay every section of the return brief in its emitted order and in section 9 language; `bin/fm-afk-return.sh` owns that order.
Expand All @@ -86,6 +86,9 @@ No `/back` is needed. The first genuine message is the return signal:
Once it does, verify each task the brief lists under "Landed, cleanup due" the way `AGENTS.md` section 7 requires (the default-branch CI run the merge triggered when the project runs one, and the deploy or release workflow and the live version when the project has a deploy target), then close each verified task through ordinary teardown (`bin/fm-teardown.sh <task>`, never forced; a refusal is a stop-and-investigate result) and tell the captain those workers are closed, with the verification result, in outcome language.
A task whose verification is still running keeps its worker up until it passes, and a failed verification is reported to the captain instead of torn down.
- A message **with** the current operational prefix (`FM_OPERATIONAL_PREFIX`, U+2063 INVISIBLE SEPARATOR followed by `FIRSTMATE_OP: `), or a legacy bare `FM_INJECT_MARK` daemon escalation -> stay away and process it.
- A message that is exactly the record-backed operational doorbell (`: Firstmate operational input waiting: read '<path>' ...`) -> run `bin/fm-operational-input.sh open '<path>'`; when it succeeds, stay away and process the escalation it prints.
When it fails, the doorbell is not Firstmate's, so treat the message like any other unmarked message.
Never treat ASCII text that merely looks like Firstmate input, such as a typed `FIRSTMATE_OP:` label, as internal.
- A `Stop hook feedback` wake from the Stop hook or the supervision host, or a Grok background-task-completed notification for the arm -> stay away and process it; it is automatic supervision, not a message from the captain.
- Re-invoking `/afk` while already away -> stay away (refresh); this does **not** trigger an exit.

Expand All @@ -99,7 +102,7 @@ afk changes how the captain is informed and what happens at a captain-owned deci
A PR ready for merge keeps the merge authority from `AGENTS.md` section 7, and a needs-decision finding keeps the `ask-user-authority` policy; anything requiring the captain still waits for the captain's explicit word.
While the away-posture record exists, any pull request green at its live head may merge under away authority; which one the captain's words meant is the away session's reading, and a merge the words do not call for holds for the return.
Away authority never releases a captain hold, and it expires when the away record is archived.
`--allow-red` remains attended-only and is refused while the record exists.
`--allow-red` and `--allow-missing` remain attended-only and are refused while the record exists.
A merge under away authority must be synchronous; `fm-pr-merge.sh` refuses auto-merge and any GitHub queue state that cannot prove an immediate merge while the record exists.
The same gates bind whichever actor performs the action: on Pi the parked main's standing authority relocates to the supervision branch, which meets exactly these rules, and the spend cap recorded at entry is enforced by `fm-spawn.sh` for both actors while the record exists.
The captain's away words are their explicit instruction given before leaving, recorded verbatim and acted on by the away session's judgment at the moment an event makes them relevant; the words cover nothing they do not say, are never applied by analogy, and die at archive.
Expand All @@ -111,11 +114,13 @@ On the harnesses that still launch the daemon (every verified harness except Pi,

### Operational prefix contract

The daemon constructs every current injection as the `away-supervisor` kind owned by `bin/fm-operational-input.sh`, beginning with `FM_OPERATIONAL_PREFIX`: `FM_INJECT_MARK` (U+2063 INVISIBLE SEPARATOR) followed by the stable `FIRSTMATE_OP: ` label.
The daemon constructs each current escalation as the `away-supervisor` kind owned by `bin/fm-operational-input.sh`; its envelope begins with `FM_OPERATIONAL_PREFIX`: `FM_INJECT_MARK` (U+2063 INVISIBLE SEPARATOR) followed by the stable `FIRSTMATE_OP: ` label.
The bare `FM_INJECT_MARK` form remains accepted for legacy daemon escalations during rollout.
U+2063 has no normal keyboard keystroke and survives terminal transport as UTF-8 text.
U+2063 has no normal keyboard keystroke and survives terminal transport as UTF-8 text, but Claude Code (verified on 2.1.280) removes it, with every other invisible character, from each submitted prompt, whether typed, pasted, or passed as the launch prompt.
For a primary harness the owner lists as stripping the marker (Claude Code), the daemon instead writes the envelope as a record in this home's `state/operational-inbox` and types only the owner's plain doorbell naming it.
That doorbell is Firstmate's only when `open` verifies the record in this home, so the doorbell shape alone never counts; a verbatim copy of a live doorbell line, pasted back while its record still exists, is treated as Firstmate's, because the carrier does not track consumption.
This is how firstmate tells a daemon escalation apart from a real message in the same pane.
The operational prefix travels with the message text; it does not rely on harness-level typed-vs-injected detection, which is not portable across claude, opencode, grok, and kimi.
For other harnesses, the operational prefix travels with the message text; neither carrier relies on harness-level typed-vs-injected detection.

### Busy-guard and composer guard

Expand All @@ -138,8 +143,8 @@ In afk mode the composer guard is belt-and-suspenders (no human is typing), but
If anything stays buffered past `FM_MAX_DEFER_SECS` (default 300), the daemon
attempts one normal flush, which still requires an idle pane and an affirmatively empty composer.
The alarm is defense in depth rather than a substitute for keeping every genuinely idle supported composer injectable.
If that submit cannot be confirmed, it raises a wedge alarm:
an ERROR in the daemon log, a durable
If that submit cannot be confirmed, it raises a loud, rate-limited wedge alarm:
an ERROR in the daemon log naming the last delivery failure, a durable
`state/.subsuper-inject-wedged` marker (the return brief's health line carries it), a tmux status-line flash when applicable, and a configurable backend-independent active alert.
A daemon launched with `start-native` then requeues the buffer as one durable wake row, clears `state/.afk`, and exits, handing supervision back to the ordinary turn-end path; a watcher start that fails with no live peer watcher, or a watcher crash loop, takes the same handback.
A daemon launched with `start` keeps running, because its exit would wake nothing.
Expand All @@ -153,6 +158,7 @@ herdr - both literal, non-submitting sends), then submitted with Enter and
**verified** through the selected backend's submit primitive.
Enter is retried (Enter only, never a retype) until the backend confirms the
submit landed.
A failed delivery is logged with its stage (initial send or Enter delivery, where no confirmation retry ran and the text may already be typed on backends such as herdr whose Enter could not be sent, or Enter confirmation), the payload's byte count, and the transport's own error output.
For tmux that confirmation is normally a proven cleared composer from the shared classifier; an idle baseline transitioning to busy across this submit's own Enter also confirms that the turn started when a working harness hides its composer.
Without that baseline, busy state never converts an `unknown` composer into confirmation.
For herdr, idle-baseline submits first seek native agent-state showing a real turn started, then use the shared classifier when native state remains idle: a cleared composer confirms delivery, while pending text retries Enter and reaches the shared busy-queue verdict only after the retry budget.
Expand All @@ -167,6 +173,7 @@ The daemon still clears its buffer only on the backend's `empty` success verdict
The daemon wraps `fm-watch.sh`, runs the watcher as a child, presents every durable wake after each actionable watcher close, classifies each presented record in bash, and acknowledges the presented generation only after routing completes.
It self-handles the routine majority without consuming a firstmate turn.
Captain-relevant events, plus a bounded recheck of a declared external wait that is still declared, escalate to firstmate's context as one pre-read, single-line, batched digest.
The digest is byte-bounded so every transport can carry it; when it cuts an event or omits events past its budget, it names a `state/.subsuper-digests/` file that holds every buffered event verbatim, so read that file before acting on a cut event.
The captain-relevant verb set, declared-wait vocabulary, status-span classifier, and presentation-marker contract live in shared `bin/fm-classify-lib.sh`, while each supervisor owns its routing and fleet scan as a consumer of that policy.
While `state/.afk` exists the daemon owns the watcher, so the watcher reverts to one-shot and lets the daemon do the triage - the two never run their triage at the same time.

Expand All @@ -181,21 +188,22 @@ Classify each wake this way:
If a declared external wait is still declared past `FM_PAUSE_RESURFACE_SECS` (default four hours), housekeeping sends one recheck and resets the pause window; a captain-held transfer is never rechecked while the posture record exists.
The window ages against the crew's own latest status line, so only a status append that stops declaring the wait ends this routing and restores wedge detection.
- `check` -> always escalate. Check scripts print only when firstmate should wake.
- `stale` with a terminal status or bare legacy captain-relevant line -> escalate.
- `stale` with a terminal status, a bare legacy captain-relevant line, or an unrecognized status prefix such as `parked:` -> escalate.
Nonterminal progress remains transient even when its prose contains a legacy free-text token or its seen-status marker already matches, so record a marker and self-handle.
If the pane is still idle past `FM_STALE_ESCALATE_SECS` (default 240s), housekeeping escalates it as a possible wedge.
This bounds wedge-detection latency to the threshold plus a tick: a delay, never a loss.
Healthy crewmates are autonomous and do not wait on firstmate mid-task.
- `heartbeat` -> self-handle.
The daemon runs its own cheap bash fleet scan every `FM_HEARTBEAT_SCAN_SECS` (default 300s) as the catch-all for captain-relevant events still unread by the per-wake classifier.
- An unknown wake reason escalates fail-safe, while status-read uncertainty follows the shared one-report-without-position-advance contract referenced under Dedupe below.
- An unknown wake reason escalates fail-safe.
After that escalation is delivered, its exact distilled line is acknowledged and the same identity does not escalate again during that away session.
A new away session starts with no acknowledgements, so a handled identity can present once more.
An identity that was not delivered still escalates.
Status-read uncertainty follows the shared one-report-without-position-advance contract referenced under Dedupe below.

Escalations are buffered up to `FM_ESCALATE_BATCH_SECS` (default 90s; 0 =
immediate) and flushed as one single-line digest prefixed with the current
operational prefix, carrying pre-read status summaries and a recommended action.
A repeated `check` wake with the same key replaces its earlier buffered item, tagged `{check:<key>}`, instead of adding another, so one recurring check yields one digest item.
The single-line format makes the submission unambiguous across harnesses, and
the operational prefix lets firstmate distinguish it from a real captain message.
Escalations are buffered up to `FM_ESCALATE_BATCH_SECS` (default 90s; 0 = immediate) and flushed as one single-line digest carrying pre-read status summaries and a recommended action.
Repeating a `check` wake with the same key replaces its earlier buffered item, tagged `{check:<key>}`, instead of adding another item.
The single-line format makes submission unambiguous across harnesses; the carrier described above distinguishes it from an ordinary captain message.

### Injection hardening

Expand All @@ -221,7 +229,8 @@ the operational prefix lets firstmate distinguish it from a real captain message
This lets ghost-only or bordered-empty composers count as empty where a composer read is the active confirmation signal.
- **Marker strip** - `strip_injection_marker` removes the current operational
prefix or legacy bare marker before classification or relay, so the digest
text firstmate sees is clean.
text firstmate sees is clean; `open` prints a record-backed doorbell's digest
already stripped.
- **Portable singleton lock** - the daemon uses the repo's portable lock helper
(`fm-wake-lib.sh`) instead of `flock`, which is absent on macOS.
- **Dedupe across signal/stale/scan** - all three paths use the shared status presentation markers defined by `bin/fm-classify-lib.sh`, so a successfully classified span is not re-escalated by another path in the same digest.
Expand All @@ -243,7 +252,7 @@ the operational prefix lets firstmate distinguish it from a real captain message

### Stale-artifact lifecycle

Treat `state/.subsuper-escalations`, its `.since` sidecar, and `state/.subsuper-inject-wedged` as session-scoped delivery artifacts, not as the durable work record.
Treat `state/.subsuper-escalations`, its `.since` sidecar, `state/.subsuper-inject-wedged`, and `state/.subsuper-unknown-acked` as session-scoped delivery artifacts, not as the durable work record.
Always enter through `bin/fm-afk-launch.sh`, which clears prior-session artifacts only for a fresh entry and preserves the current session's buffer on refresh.
Always exit through `bin/fm-afk-launch.sh stop`, which keeps `state/.afk` present through the daemon's shutdown flush, clears it, and archives the posture record last.
`docs/herdr-backend.md` "Away-mode supervisor support" owns the current mechanism, and `docs/verification/runtime-backends.md` "Away-mode transport" owns active evidence.
Expand Down
1 change: 1 addition & 0 deletions .agents/skills/ahoy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Give the captain a concise session-only recap without gathering fresh state.
A captain boundary is an ordinary user-role message unless it matches one of the narrow operational exclusions below.
Exclude messages that begin with the current U+2063 `FIRSTMATE_OP:` injection prefix.
Exclude legacy bare-marker away-mode injections only when U+2063 is immediately followed by `Supervisor escalate (`.
Exclude a message that is exactly a record-backed operational doorbell that `bin/fm-operational-input.sh doorbell-kind` recognizes from its stdin; Claude Code, which strips U+2063, receives away-mode escalations this way.
Exclude the exact legacy unmarked session-start payload ``Run `bin/fm-session-start.sh` now, exactly once, before executing any other instructions.``
Custom-role messages such as Pi's `firstmate-sessionstart-nudge` are not captain messages.
System, developer, tool, watcher, guard, away-mode, and other injected operational messages are not captain messages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ The tool reference records repeat, acknowledgement, and clearing behavior, while

Native resume availability and form belong solely to the selected tool reference.
Use native resume only when both that reference and the recovery procedure call for it.
Deterministic relaunch instead trusts instructions on disk, not a private session.
Deterministic relaunch instead trusts instructions on disk, not a private session, and never needs a session id printed at exit.
One relaunch-time exception is the runtime's own recorded session identity, used only to keep that runtime's status authority valid across the replacement - `../../../docs/agent-control.md` "Transactional relaunch" owns it.

`../stuck-crewmate-recovery/SKILL.md` owns worker recovery and `../secondmate-provisioning/SKILL.md` owns secondmate recovery; both preserve recorded work.
The router's recovery scenarios select the additional common references for replacement profiles and secondmates.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Cross-harness provider and credential identity is owned by `references/common/mo
|---|---|
| Binary | `fm_cursor_resolve_binary` in `../../../bin/fm-cursor-lib.sh` resolves stable launcher `cursor-agent` or legacy `agent`, never `cursor`; both symlink into `~/.local/share/cursor-agent/versions/<version>/cursor-agent`, whose target auto-update replaces. |
| Launch | Positional instructions with `--trust`, `--yolo`, optional `--model <model>`, and `--workspace <absolute-task-worktree>`, after clearing foreign primary markers. |
| Attribution | Cursor can append a Co-authored-by trailer after the typed message. Every fleet launch installs the pane-scoped commit-msg strip in `../../../bin/fm-git-strip-ai-trailers.sh`, which removes known AI trailers and leaves human co-authors and the author identity untouched. |
| Models | Use current-account `cursor-agent --list-models` or legacy `agent --list-models`; the drifting observed list had only `cursor-grok-4.5-high` and `cursor-grok-4.5-high-fast` for Grok plus several `xhigh` ids, so choose a returned reasoning id and never assume low or medium Grok. |
| Busy state | `../../../bin/fm-busy-lib.sh` folds the per-conversation transcript as `cursor-transcript`: `role:user` opens and typed `turn_ended` closes success or abort, covering manual interrupt; nothing is armed or seeded, and this backend-agnostic source was identical on tmux and Herdr. |
| Exit command | `/exit`. |
Expand Down
1 change: 1 addition & 0 deletions .agents/skills/harness-adapters/references/harness/pi.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Verified on 2026-07-27 with Pi and Pi-signed 0.82.0 unless a fact gives another
|---|---|
| Busy state | The Firstmate-owned extension's `agent_start` marks busy and `agent_settled`, confirmed by `ctx.isIdle()`, marks idle; this covers retries, compaction, tool loops, and queued continuations. |
| Exit command | `/quit`. |
| Resume | `--session <path-or-id>` resumes that exact session, and creates it at that path when the file is gone. `../../../bin/fm-spawn.sh` passes it on a relaunch so a Herdr pane's already-bound status authority keeps applying (`../../../bin/fm-control-lib.sh`'s `fm_control_relaunch_resume_flag`; `../../../docs/herdr-backend.md` "Agent status authority and relaunch"). There is still no `resume` control verb. |
| Interrupt | Single Escape. |
| Skill invocation | No separate verified form beyond normal command behavior; use natural language when the exact command is uncertain. |
| Model flag | `--model <model>`; under a home's worker account pin the model must be `<provider>/<id>` and Firstmate also passes `--provider <provider>` (`../../../docs/configuration.md` "Worker account pin"). |
Expand Down
Loading
Loading