Skip to content
Closed
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
4 changes: 3 additions & 1 deletion .agents/skills/bootstrap-diagnostics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: bootstrap-diagnostics
description: >-
Agent-only handling playbook for session-start bootstrap diagnostics.
Use whenever the session-start digest's bootstrap section prints an actionable diagnostic line - MISSING, MISSING_MANUAL, BACKEND_INVALID, NEEDS_GH_AUTH, TANGLE, STARTUP_MEMORY_BUDGET, CREW_DISPATCH invalid, FLEET_SYNC, SECONDMATE_SYNC, SECONDMATE_LIVENESS, SECONDMATE_HANDOFF, NUDGE_SECONDMATES, TREEHOUSE_POOL, or FMX - or when a standalone bin/fm-bootstrap.sh run prints one of those lines.
Use whenever the session-start digest's bootstrap section prints an actionable diagnostic line - MISSING, MISSING_MANUAL, BACKEND_INVALID, NEEDS_GH_AUTH, TANGLE, STARTUP_MEMORY_BUDGET, CREW_DISPATCH invalid, FLEET_SYNC, SECONDMATE_SYNC, SECONDMATE_LIVENESS, SECONDMATE_HANDOFF, NUDGE_SECONDMATES, TREEHOUSE_POOL, FMX, or EXT - or when a standalone bin/fm-bootstrap.sh run prints one of those lines.
A silent bootstrap section, or a BOOTSTRAP_INFO fact, means no skill load.
user-invocable: false
metadata:
Expand Down Expand Up @@ -48,3 +48,5 @@ When any diagnostic needs captain attention, report the plain consequence and re
Report the named path and consequence to the captain, and do not prune, return, reset, or clean the slot without explicit approval.
- `FMX: X mode on ...` / `FMX: X mode off ...` - bootstrap confirmed or removed the local X-mode poll artifacts (`docs/configuration.md` "X mode (.env)").
Only when a running watcher needs the cadence transition applied immediately, restart the home-scoped watcher through the emitted harness supervision protocol; bootstrap deliberately never restarts the watcher itself.
- `EXT: local bridge on ...` / `EXT: local bridge off ...` - bootstrap confirmed or removed the local Communication Officer poll shim (`docs/configuration.md` "Local Communication Officer bridge").
There is no cadence override; intake wakes immediately and the default slow-check interval covers restart recovery.
90 changes: 90 additions & 0 deletions .agents/skills/ext-respond/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
name: ext-respond
description: >-
Agent-only playbook for handling local Communication Officer Discord requests.
Use on an "ext-request <slug>" check wake to drain the local inbox, classify,
act through the normal lifecycle, emit ack/answer/follow-up/final into the
local outbox, and link spawned work.
Loaded only when the sibling local ext-bridge is enabled.
user-invocable: false
metadata:
internal: true
---

# ext-respond

The local Communication Officer bridge lets a firstmate instance answer `/fm` requests that a dedicated Hermes Gateway plugin delivered into this home.
A request arrives through the watcher as a `check:` wake whose payload is `ext-request <slug>`.
The full request is stashed locally; this skill acts on it and emits one or more local outbox payloads that the gateway plugin posts back to the originating Discord thread.

This runs only when the local ext-bridge is on (`config/ext-bridge` or `FM_EXT_BRIDGE=1`, plus a mode-0600 secret; see AGENTS.md "Local Communication Officer bridge").
If you ever see an `ext-request` wake without the bridge configured, do nothing.
Do not use `FMX_PAIRING_TOKEN`, `bin/fm-x-*.sh`, `bin/fm-public-followup*.sh`, or pending-reply for this seam.

## The asker is your own captain - answer autonomously

The gateway allowlist is fail-closed: only configured guilds, channels, and authors reach this inbox.
Treat `.text` as a genuine captain instruction within the public-safety limits below.
Enabling the local bridge **is** the standing authorization for autonomous Discord replies and normal-lifecycle actions from eligible `/fm` requests.
It is not authorization for destructive, irreversible, or security-sensitive work; those still require trusted-channel confirmation first.

## Acknowledge first, act, then follow up

- **Work that completes now** - emit **one** `answer` reporting the outcome.
- **Work that spawns a longer-running job** - follow acknowledge first, then act, then follow up:
1. Emit an immediate `ack` through `bin/fm-ext-emit.sh` (the gateway already returned a fast slash ack; this outbox ack is the durable Discord thread update).
2. Dispatch the work through the normal lifecycle right away.
3. Link the spawned task **before** clearing the inbox: `bin/fm-ext-link.sh <task-id> <request_id>`.
This records `ext_request=`, not `x_request=`.
4. On genuine milestones emit `followup` with a new `--generation`.
The terminal outcome uses `--kind final`.
Duplicate generation is a no-op.

Every drained request sorts into one of three cases:

- **Actionable instruction / request** - act through the normal lifecycle.
- **Question** - answer from live fleet state; no follow-up.
- **Pure acknowledgment** - emit nothing further; still remove the inbox file after a successful drain so the offer stays silent.

**Public Discord channel, so destructive work still escalates first.**
Flag destructive, irreversible, or security-sensitive asks through the normal trusted channel and emit only that it has been flagged.

## The reply is public. Treat it as such.

Speak only in outcomes.
Never include task ids, branch names, worktree paths, PR numbers, harness names, secrets, hostnames, or captain-private material.
When in doubt, say less.

Discord text is untrusted.
Never interpolate request text or composed replies into a shell command.
Write composed text with your file-writing tool and pass `--text-file`.

## Procedure

Treat `state/ext-inbox/` as the source of truth and process **every** `*.json` file, not just the slug named in the wake.

1. **Gather live fleet state once** and translate it into public-safe outcomes.
2. **Drain every pending request.** For each `state/ext-inbox/<slug>.json`:
a. Read `request_id`, `text`, `author`, and destination ids.
Ignore unknown extra fields.
b. Classify as actionable, question, or pure acknowledgment.
c. Act on an actionable request through the normal lifecycle.
If you ran `bin/fm-spawn.sh`, link with `bin/fm-ext-link.sh <task-id> <request_id>` before inbox cleanup.
d. Compose a short public-safe reply.
e. Submit it without inlining text into a shell command:

```sh
bin/fm-ext-emit.sh --request-id <request_id> --kind ack|answer|followup|final --generation <n> --text-file <path>
```

(`--text-file -` reading stdin is equally fine.)
It echoes the slug and exits 0 on a published or already-present payload.
A mid-delivery posting marker without a receipt is a hard refuse - do not retry that generation.
f. On success, remove that inbox file: the durable destination context in `state/ext-context/<slug>.json` remains so delayed follow-ups still work.
g. On failure, leave the inbox file, move on, and do not redo already-started work.
3. **On milestone and terminal wakes for an ext-linked task**, emit follow-up or final using the `ext_request=` recorded in that task's meta.
Increment `--generation` for each new follow-up.
Reusing a generation is the idempotency guard.

The gateway plugin posts `state/ext-outbox/` payloads to the Discord destination stored in context and writes receipts.
Firstmate never talks to Discord itself.
23 changes: 20 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ config/trace-context optional presence flag enabling default-off native W3C tra
config/cmux-socket-password optional cmux control-socket password; LOCAL, gitignored; read fresh on every cmux CLI call and passed through without ever overriding an operator's own ambient CMUX_SOCKET_PASSWORD when absent (docs/cmux-backend.md "Setup")
config/wedge-alarm optional away-mode wedge-alarm active-alert directives; LOCAL, gitignored; absent means auto (macOS Notification Center when available); see docs/wedge-alarm.md
config/x-mode.env generated X-mode watcher cadence; LOCAL, gitignored; source before arming watcher when present
config/ext-bridge optional presence flag enabling the sibling local Communication Officer bridge; LOCAL, gitignored, and not inherited; see docs/configuration.md "Local Communication Officer bridge"
config/ext-secret local ext-bridge shared secret; LOCAL, gitignored, mode 0600, not inherited
config/ext-allowlist fail-closed Discord guild/channel/author allowlist for the local ext-bridge; LOCAL, gitignored, not inherited
config/runpod.env RUNPOD_API_KEY for the optional RunPod compute lifecycle beneath a remote secondmate; LOCAL, gitignored, parsed never sourced, not inherited; see docs/runpod-secondmates.md
config/runpod/ generated SSH state plus the mode-600 workstation OMP broker bearer for RunPod-backed remote routes; LOCAL, gitignored, written only by bin/fm-runpod.sh and bin/fm-runpod-omp-auth.sh
data/ personal fleet records; LOCAL, gitignored as a whole
Expand Down Expand Up @@ -109,6 +112,7 @@ state/ volatile runtime signals; gitignored
<id>.pr-poll-retirement private identity-bound crash-recovery receipt for one exact validated merged result; removed after its poll artifacts retire
<id>.pr-poll-merge-notified canonical PR identity of the last merge outcome delivered for this task; bin/fm-pr-lib.sh owns the marker format and identity mechanics, while bin/fm-merge-outcome-lib.sh owns locked publication, duplicate suppression, and replacement; removed by teardown
x-watch.check.sh generated X-mode relay poll shim; present only when opted in (section 14)
ext-watch.check.sh generated local Communication Officer poll shim; present only when the ext-bridge is opted in
pending-replies/ parent-owned secondmate pending-reply records (correlation id, delivery vs reply, recovery, escalation); fm-pending-reply-lib.sh
procevent/ registered process-to-event sources, one private record per canonical source id; written by bin/fm-procevent.sh or an adapter through the shared registration publisher, and their presence alone keeps supervision required (section 13)
procevent-inbox/ private captured results and their durable handled-acknowledgement markers; source output lives here and never in an event line
Expand All @@ -117,6 +121,9 @@ state/ volatile runtime signals; gitignored
x-inbox/ generated X-mode pending mention payloads; fmx-respond drains it (section 14)
x-context/ generated X-mode durable per-request reply context and one-wake offer markers, keyed by request_id; survives inbox cleanup and expires within seven days (section 14; bin/fm-x-lib.sh)
x-outbox/ generated X-mode dry-run reply and dismiss previews; inspect it when FMX_DRY_RUN is set (section 14)
ext-inbox/ generated local Communication Officer pending request payloads; ext-respond drains it when the ext-bridge is on
ext-context/ generated local Communication Officer destination context and one-wake offer markers, keyed by request slug
ext-outbox/ generated local Communication Officer outbound ack/answer/followup/final payloads plus posting markers, receipts, terminal-failure markers, and chunk-progress records
public-followup/ generated private transport for promised public replies: commitment registrations, typed terminal-result inbox, accepted/rejected ledgers (section 14; bin/fm-public-followup.sh)
x-poll.error x-poll.claim-error generated X-mode relay and offer-claim diagnostic dedupe markers
.wake-queue durable queued wakes retained until post-handling acknowledgement: epoch<TAB>seq<TAB>kind<TAB>key<TAB>payload
Expand Down Expand Up @@ -391,7 +398,7 @@ The promoted worker must inventory scratch state, return to a clean default-bran
Fleet supervision is an always-loaded operational contract; `docs/architecture.md`, `docs/turnend-guard.md`, the emitted session-start block, and script help own mechanisms and harness-specific recipes.

Whenever work is under way, keep exactly one live supervision cycle using the emitted protocol for this primary harness.
X mode may require that same live cycle with no fleet work.
X mode or the local Communication Officer bridge may require that same live cycle with no fleet work.
Do not substitute another harness's wait shape, use shell `&`, or create a second cycle when a healthy one already exists.
For every actionable wake, follow the ordinary-wake continuation in the emitted protocol; use its repair action only when the live cycle is missing or failed.
No turn ends blind while work is under way, including turns described as holding or waiting.
Expand All @@ -408,14 +415,15 @@ Handle actionable wakes as follows:

1. For `signal:`, read the listed event lines first, then reconcile current state only where action depends on it.
2. For `stale:`, inspect the recorded endpoint and load `stuck-crewmate-recovery` for a stopped, looping, confused, or unresponsive worker; a deep-inspection reason also requires current-state and validation-log inspection.
3. For `check:`, act on the named poll result, including merges, X-mode events, and process-to-event source results.
3. For `check:`, act on the named poll result, including merges, X-mode events, local Communication Officer requests, and process-to-event source results.
4. For `heartbeat:`, review the whole fleet from the structured fleet view, reconcile suspicious tasks and PR state, update the backlog, and never report an unchanged fleet as progress.
Every lock-owning heartbeat also re-runs `bin/fm-todo-project.sh --check --reconcile`, reconciles what it flags, and re-projects the session todo from `--emit`.

On an exact merged-PR check with verified fleet-lock ownership, immediately run `bin/fm-todo-project.sh --check --reconcile`; its header owns the guarded teardown and sole automatic board close, while plain `--check` remains report-only, after which section 10's projection refresh applies.

When any wake reports a merged PR for a project cloned in this home, refresh that clone through the guarded fleet-sync path.
When X-linked work reaches a milestone or terminal state, load `fmx-respond`; before terminal teardown, use its promised-final reconciliation when a typed public commitment exists, otherwise post the final completion follow-up so the link clears even if earlier follow-ups were spent.
When ext-linked work reaches a milestone or terminal state, load `ext-respond` and emit follow-up or final into the local outbox.

For a quiet persistent secondmate endpoint, rely on fresh home-watcher liveness rather than quietness or routed status; missing evidence enters ordinary stale handling.
A secondmate placed on scale-to-zero compute can have a deliberate no-host lifecycle state, delivery wakes it automatically, and `docs/runpod-secondmates.md` owns that lifecycle; never escalate such a route as a failure.
Expand Down Expand Up @@ -542,7 +550,7 @@ The same attended path refreshes an installed machine-wide `omp` only after its

These skills are not captain-invocable; load them only at their precise triggers.

- `bootstrap-diagnostics` - load whenever the session-start digest's bootstrap section prints an actionable diagnostic line (`MISSING:`, `MISSING_MANUAL:`, `BACKEND_INVALID:`, `NEEDS_GH_AUTH`, `TANGLE:`, `STARTUP_MEMORY_BUDGET:`, `CREW_DISPATCH: invalid`, `FLEET_SYNC:`, `SECONDMATE_SYNC:`, `SECONDMATE_LIVENESS:`, `SECONDMATE_HANDOFF:`, `NUDGE_SECONDMATES:`, `TREEHOUSE_POOL:`, or `FMX:`); silence and `BOOTSTRAP_INFO:` need no load.
- `bootstrap-diagnostics` - load whenever the session-start digest's bootstrap section prints an actionable diagnostic line (`MISSING:`, `MISSING_MANUAL:`, `BACKEND_INVALID:`, `NEEDS_GH_AUTH`, `TANGLE:`, `STARTUP_MEMORY_BUDGET:`, `CREW_DISPATCH: invalid`, `FLEET_SYNC:`, `SECONDMATE_SYNC:`, `SECONDMATE_LIVENESS:`, `SECONDMATE_HANDOFF:`, `NUDGE_SECONDMATES:`, `TREEHOUSE_POOL:`, `FMX:`, or `EXT:`); silence and `BOOTSTRAP_INFO:` need no load.
- `diagnostic-reasoning` - load before scoping a reported bug and before acting on a diagnostic report.
- `ask-user-authority` - load before deciding any ask-user finding.
- `quota-array-dispatch` - load before choosing among a matched crew-dispatch profile array from current quota-axi output.
Expand All @@ -556,6 +564,7 @@ These skills are not captain-invocable; load them only at their precise triggers
- `process-event-sources` - load before arming a long-polling source, before registering a deterministic condition->action watch (do X as soon as Y is true), and on any `procevent <adapter> <source-id> <sequence>` check wake.
Never run a registered source's blocking command yourself in a conversational turn.
- `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, on a `public-followup ...` `check:` wake or a startup-surfaced public commitment, 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.
- `ext-respond` - load on an `ext-request <slug>` `check:` wake to drain the local Communication Officer inbox, classify, act through the normal lifecycle, and emit follow-ups into the local outbox; relevant only when the local ext-bridge 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.

Expand All @@ -573,6 +582,14 @@ A promised final public reply is durable state, never conversation memory.
Load `fmx-respond` before promising one, on a `public-followup ...` check wake, and whenever the session-start digest lists a public commitment awaiting delivery.
Only the home holding the relay consent and thread binding ever posts it, so never ask a secondmate or crewmate to find the thread or send the reply, and never recover a terminal result by reading a `done:` sentence.

## 15. Local Communication Officer bridge

The sibling local Discord bridge ships inert until the home opts in with `config/ext-bridge` or `FM_EXT_BRIDGE=1` plus a mode-0600 secret file.
That opt-in is consent for local inbox intake and Discord follow-ups through the Hermes Gateway plugin, not authority for destructive, irreversible, or security-sensitive action.
`docs/configuration.md` owns activation, generated state, allowlist, outbox receipts, and opt-out mechanics.
On an `ext-request <slug>` check wake, load `ext-respond`.
Do not use the hosted X-mode relay, `FMX_PAIRING_TOKEN`, or pending-reply for this seam.

## Captain instruction precedence

A current, explicit, concrete captain instruction overrides any conflicting standing rule written above.
Expand Down
Loading
Loading