Features: auto-compact window (CB-636) + cross-host lead coordination (CB-637)
Add two Features entries and propagate the cross-host peer-lead coordId row into the canonical CLAUDE.md block template (7-Use-Cases). The block stays byte-identical with the copy in the fleetd repo's CLAUDE.md.
+61
@@ -48,6 +48,8 @@ six weeks, and the table alone will not carry it.
|
||||
| [Keep a worktree that still holds work](#keep-a-worktree-that-still-holds-work) | automatic | CB-576 | `session/GitWorktrees` |
|
||||
| [Fail a ticket when its member dies](#fail-a-ticket-when-its-member-dies) | `health.enabled: true` | CB-580 | `health/FleetHealthMonitor` |
|
||||
| [Durable reply inbox](#durable-reply-inbox) | `broker:` | CB-307 | `msg/AmqpReplyInbox` |
|
||||
| [Set a member's auto-compact window](#set-a-members-auto-compact-window) | per-profile `autoCompactWindow:` | CB-636 | `member/ClaudeCodeLauncher`, `member/OpenCodeLauncher` |
|
||||
| [Leads on different hosts talk over a shared broker](#leads-on-different-hosts-talk-over-a-shared-broker) | `coordinator:` | CB-637 | `msg/LeadMailbox`, `msg/LeadCoordLoop` |
|
||||
| [Broker password out of the config](#broker-password-out-of-the-config) | `broker.uriEnv:` | CB-635 | `config/FleetConfig` |
|
||||
| [An unreachable broker does not stop the daemon](#an-unreachable-broker-does-not-stop-the-daemon) | (always on) | CB-635 | `Fleetd.selectReplyInbox` |
|
||||
| [Reject overlapping rendezvous](#reject-overlapping-rendezvous) | automatic | CB-548 | `msg/Rendezvous` |
|
||||
@@ -2045,6 +2047,65 @@ forge; they are deliberately local and invisible to `git branch`.
|
||||
|
||||
---
|
||||
|
||||
## Set a member's auto-compact window
|
||||
|
||||
**What.** A profile can set `autoCompactWindow: <tokens>`, the context size at which a spawned
|
||||
member compacts on its own. For a `claude-code` member it becomes `--autocompact <tokens>` at
|
||||
launch. For an `opencode` member it becomes a per-model `provider.<p>.models.<m>.limit.context` in
|
||||
the generated config. Unset ⇒ the backend's own default (Claude Code's built-in auto-compact,
|
||||
opencode's `compaction.auto`).
|
||||
|
||||
**On.** Per profile in `bridged.yaml`, e.g. `autoCompactWindow: 250000`. The value must be in
|
||||
`[100000, 1000000]` — the band Claude Code's flag accepts — and the daemon refuses to start with a
|
||||
profile outside it, naming the profile. For opencode the key only takes effect when `model:` is in
|
||||
`provider/model` form (a warning is logged otherwise), because the limit is written under that exact
|
||||
provider and model.
|
||||
|
||||
**Why.** A member that runs out of context dies mid-turn and loses its `fleet_reply`, so the report
|
||||
is gone even though the work was done. The backends already auto-compact, but the *window* was not in
|
||||
the operator's hands — a long delegation on a big model could compact too late. This makes the
|
||||
trigger a per-profile choice, so an expensive profile can be told to compact earlier than a cheap one.
|
||||
|
||||
**Gotcha.** The two backends take the window by different means. Claude Code's `--autocompact` is an
|
||||
absolute token count and outranks env and settings. opencode has no absolute "compact at N" knob at
|
||||
all; the only lever is the model's `limit.context`, and the generated config also writes
|
||||
`limit.output` (16384) because opencode's schema requires both. On a gateway profile that is a
|
||||
*partial* override of the model's real limits — confirm the live member still answers after a spawn.
|
||||
Related: [opencode members run with `--auto`](#opencode-members-run-with---auto-and-forced-auto-compaction).
|
||||
|
||||
---
|
||||
|
||||
## Leads on different hosts talk over a shared broker
|
||||
|
||||
**What.** Two leads that share no herdr session — including leads on different hosts — can message
|
||||
each other by a stable coordination id. A `coordinator:` block gives this daemon one id and a durable
|
||||
AMQP mailbox `lead.<id>.inbox` on a shared vhost. `fleet_send{coordId: <peer's id>, content}`
|
||||
publishes to the peer's mailbox; the peer's daemon drains its own mailbox and types the message into
|
||||
its local lead's pane. `fleet_list` reports this daemon's own coord-id, so an operator knows the
|
||||
address to hand a peer.
|
||||
|
||||
**On.** Add a `coordinator:` block with `selfId:` (this daemon's globally-unique id) and either
|
||||
`uri:` or `uriEnv:` (the shared broker, ideally a separate vhost from the worker reply inbox). With
|
||||
no block, nothing opens and lead-to-lead stays the existing same-host pane path. If the block is
|
||||
present but `selfId` is blank, the daemon logs a warning and skips the mailbox — it does not crash.
|
||||
If the broker is unreachable at boot, the daemon warns and carries on with the feature off.
|
||||
|
||||
**Why.** [Leads talk to each other](#leads-talk-to-each-other) widened addressing, but only for leads
|
||||
that share one herdr session — the address was a bare herdr terminal id, which means nothing on
|
||||
another host. Cross-host coordination needs an address that means the same thing everywhere and a
|
||||
transport that is not a local pane. The shared broker is the one channel both hosts already reach, so
|
||||
a coord-id plus a per-lead mailbox is the smallest thing that carries a message across the gap.
|
||||
|
||||
**Gotcha.** A publish to a coord-id whose mailbox no daemon owns is reported as unroutable, not
|
||||
silently dropped — the peer must be configured and running for the message to land. Delivery into the
|
||||
local lead's pane is status-gated exactly like a worker reply: a message waits, unacked, until the
|
||||
lead pane is idle, blocked or done, so it is never typed in mid-turn. A daemon with several leads
|
||||
must *name* one lead after `coordinator.selfId`, or it cannot tell which pane a peer's message is for
|
||||
and holds it. This is transport only — there is no peer discovery yet; a lead is addressed by a
|
||||
coord-id it was already told.
|
||||
|
||||
---
|
||||
|
||||
## Backfill status
|
||||
|
||||
This page was started after the fact, so it is **not yet complete**. Entries above are written from
|
||||
|
||||
+2
-1
@@ -400,7 +400,8 @@ the merge — and merging on a reviewer's word is delegating it by proxy.
|
||||
| Delegate (blocking) | `fleet_send{sessionId, content}` |
|
||||
| Delegate (long task) | `fleet_send{sessionId, content, wait:false}` → ticket → `fleet_poll{ticket}` |
|
||||
| Answer a member's `fleet_ask` | `fleet_send{turnId, content}` — **not** `sessionId` |
|
||||
| Message a **peer lead** | `fleet_send{sessionId: <their terminal>, content}` — `fleet_list` → `leads` reports it. Coordination only, **never** a task |
|
||||
| Message a **peer lead** on this host | `fleet_send{sessionId: <their terminal>, content}` — `fleet_list` → `leads` reports it. Coordination only, **never** a task |
|
||||
| Message a **peer lead** on another daemon or host | `fleet_send{coordId: <their coord-id>, content}` — needs a `coordinator:` block; your own coord-id is in `fleet_list`. Coordination only, **never** a task |
|
||||
| Answer a peer lead that messaged you | `fleet_reply{content}` — the one case a lead replies |
|
||||
| Collect a held reply | `fleet_poll{target}` · then `fleet_ack{target, msgId}` |
|
||||
| Tear down a member | `fleet_stop{paneId}` |
|
||||
|
||||
Reference in New Issue
Block a user