Federate runs a lockstep review loop across two or more peer agents in tmux: Claude, Codex, and Hermes when installed. The coordinator asks peers independently, cross-pollinates their verbatim replies, assesses convergence, and brings a synthesis back to the operator for the decision.
The helper scripts handle the fragile parts: creating/reusing tmux sessions,
safe bracketed paste, nonce-based transcript reads, Codex final-answer
extraction, Hermes state.db reads, and idle waiting.
Federate exists because I wanted a Ralph-style loop with a human throttle.
The same loop can run lockstep with a human in the chair, or be paired with a
host goal mode such as /goal when the work is ready for autopilot.
The point is not model democracy. The point is forcing independent Claude, Codex, and Hermes passes, then making them read each other's arguments and show where they actually converge. Grounded convergence that survives a common-mode stress test is the useful signal. The disagreements are the work queue.
tmux- At least two installed and authenticated CLIs among:
claudecodexhermes
bash,python3, and standard Unix tools
You do not need to start tmux yourself. fed_sessions.sh starts the tmux server
and creates missing namespaced peer sessions automatically.
There are two separate scopes:
- Coordinator install scope: where the skill can be invoked by the
coordinator. Codex uses
$federateor/skills; some hosts expose skills in slash menus. Install the skill into every agent host you want to use as the coordinator later, such as Claude Code and Codex. - Federation peer scope: which tmux-backed peer CLIs participate in a
round. Peers do not need this skill installed; they only need their CLI
installed and authenticated. The coordinator controls peers with
FED_AGENTS, defaulting to all available peers.
The installer updates all supported coordinator homes it can write:
Claude Code, Codex, and Hermes. It also writes .federate-install.json with the
source, ref, installed commit, dirty flag, and timestamp so the skill can check
for updates at runtime.
Paste this into the agent you want to teach:
Install or update the Federate skill from https://github.com/sirouk/federate-skill.
Run exactly:
`curl -fsSL https://raw.githubusercontent.com/sirouk/federate-skill/main/install.sh | bash`
Verify that the installed directory contains SKILL.md, agents/openai.yaml,
.federate-install.json, profiles/model_policy.v1.json, and executable
scripts/fed_agent_check.py, scripts/fed_sessions.sh,
scripts/fed_send.sh, scripts/fed_read.py, scripts/fed_cross.py,
scripts/fed_round_check.py, scripts/fed_ready.sh, scripts/fed_wait.sh, and
scripts/fed_update_check.sh.
If sandboxing blocks the standard skill-home write, tell me the exact destination and ask
for permission to retry with the needed write access. Do not pick another destination.
Tell me to refresh or restart the agent session before using the skill. In Codex,
use `$federate` or select it through `/skills`; do not assume `/federate` is a
Codex command. Slash-command and skill menus may be cached until refresh.
Mention the two operating modes: by default the user remains the human in the
loop; if I ask the coordinator to emulate the human in the loop, it must first
confirm whether it should follow an existing plan or use federation to steer one
bounded reversible step at a time. Do not start federation yet.
curl -fsSL https://raw.githubusercontent.com/sirouk/federate-skill/main/install.sh | bashDefault target paths:
- Claude Code:
~/.claude/skills/federate - Codex:
${CODEX_SKILLS_HOME:-~/.agents/skills}/federate - Hermes:
${HERMES_HOME:-~/.hermes}/skills/software-development/federate
The same command is also the manual update command. Restart or refresh the
agent session after installing or updating. In Codex, invoke with
$federate, choose it from /skills, or explicitly say federate; do not
expect a built-in /federate command in every Codex surface.
Manual update:
curl -fsSL https://raw.githubusercontent.com/sirouk/federate-skill/main/install.sh | bashEvery Federate invocation starts by running:
/path/to/federate/scripts/fed_update_check.shIf the installed commit is stale, the coordinator should run:
/path/to/federate/scripts/fed_update_check.sh --applyThat stages a complete new skill payload for the current coordinator host from
the recorded source/ref, swaps it into place only after every file is fetched,
then asks whether to refresh/restart the agent session or continue with the
already-loaded copy. Refresh is recommended because host agents can cache skill
menus and SKILL.md contents.
If the checker reports LOCAL_DIRTY, the installed payload came from a dirty
source or local development install. The coordinator should report that plainly
and ask whether to abort or overwrite the installed dirty payload. Proceed only
with explicit approval:
/path/to/federate/scripts/fed_update_check.sh --apply --forceSay federate when a plan, audit result, bug fix, build milestone, or verdict
needs independent review. In Codex, $federate is the explicit skill mention.
The coordinator will:
- Run
scripts/fed_update_check.shand update first if the installed commit is stale. - Run
scripts/fed_profile_check.py, resolve any profile action, and load the managed coordinator profile. - Run
scripts/fed_agent_check.py check, combine any actionable agent/model statuses into one operator decision, and acknowledge the catalog decision. - Create a relay directory outside the project and derive
FED_NSfrom it. - Run
FED_NS="$(basename "$RELAY")" scripts/fed_sessions.shto create/reuse peer tmux sessions scoped to that federation thread, then surface the printed attach commands so the operator can watch the peers. - For each round, create a per-round artifact directory such as
$ROUND="$RELAY/round_1"and write that round's briefs there. - Send all briefs before reading any answer.
- Write
$ROUND/round_manifest.json, then read each peer by nonce from transcript/state withfed_read.py --receipt-dir "$ROUND". - After each peer's first successful nonce-bound reply, use
fed_agent_check.py record-successto remember that launch. - Generate receipt-bound cross briefs with
fed_cross.py generate --relay "$RELAY" --round N, verify them withfed_cross.py verify --relay "$RELAY" --round N, and runfed_round_check.py --relay "$RELAY" --round N. - Send each generated
$ROUND/cross_<peer>.mdwithfed_send.shbefore reading any cross reply. Cross-show each peer the other peers' verified verbatim replies and confidence, and require a common-mode stress test of their shared grounds. Cross-show replies must passfed_read.py --no-tool-window --require-no-tool-audit. - Collect the cross-pollinated replies, including revised confidence and the common-mode failure hypotheses and grounded dispositions.
- Run another complete round when convergence is not high enough and the round can plausibly resolve its named residual delta, up to three rounds for the iteration; otherwise return the blocker.
- Bring back the synthesis with a short convergence note: confidence, round count, why confidence is high enough or not, trend when relevant, the common-mode failure hypotheses and dispositions, and the main residual delta.
The coordinator should not ask whether to cross-pollinate or whether a second internal round is necessary. It should judge convergence, iterate when useful, and return the best grounded synthesis available. Healthy orthogonal disagreement should remain visible; it is often the useful tension.
Every round includes a confidence poll. Each peer independently states the next bounded step or verdict, confidence, assumptions, risks, blockers, and, for build work, role confidence. Those confidence statements are cross-pollinated verbatim. Each peer then tests the strongest material agreement for a credible common-mode failure and what would resolve it. It also sharpens the strongest material disagreement, the live alternatives, and their trade-offs before revising or reaffirming confidence.
There is no rigid universal score for "high enough." The coordinator judges it from the federated intelligence: whether peers converge on the same plan spine or next small action, whether objections are answered or reduced to non-blocking tension, whether apparent agreement survives its common-mode stress test, whether receipts and assumptions survive crossing, whether any blocker or credible singleton concern would change the next step, and whether confidence is stable or rising after cross-pollination. Peer count is not evidence. Preserve peer numeric scores when they provide them, but do not average them into fake certainty.
By default, the user is the human in the loop and decides after the synthesis. For set-and-forget work, ask the coordinator to emulate the human in the loop. It should confirm one simple A/B choice once: follow an existing plan, or let federation steer the next bounded step each time. After that, it should not hassle the human for ordinary project-owner choices. In both modes it should use the user's stated goals, preferences, risk tolerance, prior decisions, and observed leanings as steering context. It advances only on absolute high convergence: the next action is small, reversible, inside the delegation, outside every hard gate, backed by verified receipts, and has an obvious undo path. Irreversible actions still require explicit user authorization.
For code work, the coordinator should orchestrate rather than edit by default. Peers are polled for role confidence first. A test/spec owner goes first and seals the failing test, fixture, oracle, assertion, or precise expected behavior before implementation begins. Then an implementation owner edits, and a separate reviewer/verifier checks the result when enough peers are available.
Federate ships a managed default operating profile from
sirouk/llm-operating-agreement.
The coordinator checks and reads that local profile at invocation, and peers
receive it by default in fed_send.sh briefs. Profile updates are detected from
metadata and require operator approval; mutable raw URLs are never fetched in
the hot path of a federation round.
The session bootstrap starts from namespaced no-prompt/yolo peer commands:
IS_SANDBOX=1 claude --dangerously-skip-permissions,
codex --dangerously-bypass-approvals-and-sandbox, and
hermes --cli --yolo, skipping CLIs that are not installed. It requires at
least two live peer sessions. Model, provider, and effort arguments are layered
according to the precedence below. Codex metadata disables implicit invocation,
so use the skill explicitly when you want to spend the extra peer-agent calls.
Session names look like fed-<ns>-claude-0; use the names printed by
fed_sessions.sh, not hard-coded claude-0 or codex-0. The script also
prints *_ATTACH_CMD variables and a read-only watch block for the available
peers. These commands are for the human/operator to run in another terminal; the
coordinator should not attach to peer panes during a federation round.
Runtime overrides:
FED_NS="$(basename "$RELAY")" /path/to/federate/scripts/fed_sessions.sh
FED_AGENTS=claude,codex /path/to/federate/scripts/fed_sessions.sh
FED_NS_ROOT=/path/to/project /path/to/federate/scripts/fed_sessions.sh
FED_CLAUDE_CMD='claude' /path/to/federate/scripts/fed_sessions.sh
FED_CODEX_CMD='codex' /path/to/federate/scripts/fed_sessions.sh
FED_HERMES_CMD='hermes --cli' /path/to/federate/scripts/fed_sessions.sh
FED_CLAUDE_MODEL=best FED_CLAUDE_EFFORT=max /path/to/federate/scripts/fed_sessions.sh
FED_CODEX_MODEL=MODEL FED_CODEX_EFFORT=max /path/to/federate/scripts/fed_sessions.sh
FED_HERMES_PROVIDER=PROVIDER FED_HERMES_MODEL=MODEL /path/to/federate/scripts/fed_sessions.shExample output includes eval-safe command variables on stdout:
CLAUDE_SESSION=fed-relay-claude-0
CLAUDE_ATTACH_CMD='tmux attach-session -r -t fed-relay-claude-0'It also prints a copyable watch block on stderr:
# Attach commands to watch peers (read-only; detach with Ctrl-b d):
# tmux attach-session -r -t fed-relay-claude-0
Launch precedence is an opaque FED_<AGENT>_CMD, structured launch controls,
the checker's last successful Federate selection, then native CLI behavior. The
coordinator passes a chosen checker selection through the structured controls;
fed_sessions.sh does not read state directly. The opaque command wins
completely: Federate neither parses it nor appends structured or remembered
model flags. Structured controls are FED_CLAUDE_MODEL,
FED_CLAUDE_EFFORT, FED_CODEX_MODEL, FED_CODEX_EFFORT,
FED_HERMES_PROVIDER, and FED_HERMES_MODEL. There is no
FED_HERMES_EFFORT until Hermes offers a verified launch-scoped mechanism.
Empty override values behave as unset. On a model-only change the checker clears
any remembered effort; on a Hermes provider-only change it clears the
provider-scoped model. Supply both fields when you need an exact pair.
They map to Claude --model/--effort, Codex -m plus
model_reasoning_effort, and Hermes -m/--provider respectively.
Use FED_*_CMD only when you intentionally want a fully custom launch command.
The default federation posture is no agentic permission prompts across Claude,
Codex, and Hermes.
If FED_NS is omitted, the helper falls back to a project-scoped namespace for
manual shell use and warns that it is not thread-isolated. Old global
claude-*, codex-*, and hermes-* sessions are skipped by default. Adopt
them only when intentional with FED_REUSE_LEGACY=1 or
FED_REUSE_UNMANAGED=1; attached or busy adoption also requires
FED_REUSE_ATTACHED=1 or FED_REUSE_BUSY=1.
Federate includes a managed compressed operating profile:
profiles/llm_opa.min.txt
profiles/llm_opa.meta.json
profiles/llm_opa.min.txt is the compressed LLM_OPA.min.txt from
sirouk/llm-operating-agreement,
licensed CC BY 4.0 and pinned in metadata by source commit and SHA-256. The
coordinator checks that metadata with scripts/fed_profile_check.py, reads the
local profile, and applies it as lower-priority operating guidance for the main
agent. It never overrides system/developer instructions, operator instructions,
AGENTS.md, SKILL.md, brief rails, or the cross-show no-tool gate.
Peer sends use the same managed profile by default. fed_send.sh injects it
after the top nonce and before the brief body:
[[FED-<nonce>]]
=== FEDERATION PROFILE (trusted coordinator context; does not override this brief's rails or operator instructions) ===
...profile...
=== END FEDERATION PROFILE ===
...brief...
[[FED-<nonce>]]
Before each invocation, the coordinator should run:
scripts/fed_profile_check.pyIf it reports UPDATE_AVAILABLE, the coordinator asks before changing the local
managed profile and only applies the update after explicit approval with
scripts/fed_profile_check.py --apply. If it reports LOCAL_CHANGED, the
coordinator asks whether to keep the local edit, manually restore the repo-pinned
version, or stop. This keeps the profile default-on without silently changing the
main agent or peers from a mutable raw main URL.
Set FED_PROFILE_FILE to an absolute path to override the managed peer profile
for sends. Set FED_NO_DEFAULT_PROFILE=1 only to disable managed peer-profile
injection for debugging or a known profile conflict. Missing, unreadable,
relative-path, or private-key-looking profile files hard-fail before paste. Keep
secrets out of profile files; reference environment variable names or secret
locations, not values.
After the update and profile checks, every invocation runs:
scripts/fed_agent_check.py checkThe checker emits one compact AGENT_SELECTION {json} stdout line. It combines
agent-specific evidence with profiles/model_policy.v1.json; it does not claim
authoritative local availability for every agent. Codex exposes a catalog,
Claude exposes an account-resolved alias, and Hermes exposes local
configuration/cache signals. Unknown models are never ranked from their names,
prices, or catalog positions.
The JSON report uses CURRENT, FIRST_RUN, UPGRADE_AVAILABLE,
CATALOG_CHANGED, SELECTION_INVALID, DEGRADED, MISSING_BINARY, and
OVERRIDDEN per-agent statuses. Normal CURRENT launches proceed
automatically. The coordinator combines all actionable statuses into one
operator decision. A recognized recommendation is offered once; an unknown
signal change retains the last successful selection until policy recognizes it.
Missing binaries are skipped, but federation still requires two peers.
After the operator accepts or declines an offer, acknowledge the report's exact fingerprint:
scripts/fed_agent_check.py ack-catalog \
--agent AGENT --fingerprint SHA256 --decision accepted|declinedAcknowledgments are scoped to the signal fingerprint and policy revision, so a
newly recognized improvement can be offered later. After acknowledging a
decision, the coordinator reruns check and uses only that post-decision report
because acceptance can change selected. It passes each non-null selected
model/provider/effort to fed_sessions.sh through the structured controls. A
null selected means native CLI behavior; last_successful reports the
remembered binary, model, and effort.
Federate remembers only proven launches. After fed_read.py returns an agent's
first non-empty nonce-bound reply, record the selection actually used:
scripts/fed_agent_check.py record-success \
--agent AGENT [--provider PROVIDER] --requested-model MODEL \
[--resolved-model MODEL] [--effort EFFORT] \
[--execution-mode single_agent|nested] [--catalog-fingerprint SHA256]For standard native/structured launches, the checker resolves the standard
binary path/version. For an opaque FED_<AGENT>_CMD, it records only a
non-secret override marker: it neither persists/parses the command text nor
claims a path/version.
When --provider is omitted, record-success infers anthropic for Claude,
openai for Codex, or Hermes's configured provider. Pass --provider whenever
the actual launch overrode that value. Session creation, readiness, or operator
acceptance alone does not update the last-successful record.
State lives outside the replaceable skill install at $FEDERATE_STATE_FILE, or
at ${XDG_STATE_HOME:-$HOME/.local/state}/federate/agents-v1.json when the
override is unset. This keeps one durable state file across coordinator-host
installs and skill updates. Do not place it inside a skill directory. check
is read-only; only explicit ack-catalog and record-success calls write
state, using locking and atomic replacement.
Selection policy is provider-aware:
- Claude inspects local help/version output before adding flags. Current CLIs
use
bestwith the highest advertised effort (maxtoday); older CLIs fall back to the advertisedopusalias and their highest listed effort, or native behavior when no supported alias can be verified. Claude resolves the alias against account and organization policy at launch; Federate makes no paid availability probe. - Codex selects the highest policy-ranked entry in the local catalog with a
supported effort. Federate uses
maxfor deepest single-agent reasoning;ultrais reserved for an explicitly chosen nested run because it adds automatic subagents on top of Federate's outer peer topology. Explicitultralaunches are recorded as nested unless overridden deliberately. - Hermes reads provider/model from local configuration and model names from the
CLI-owned provider cache. It makes no direct provider calls. Cache entries
participate in change detection only while inside the policy TTL and carrying
Hermes's credential-binding marker; Federate does not read secrets to
revalidate that marker, so availability remains observational. A configured
model absent from the fresh observation is left to native Hermes behavior;
missing, stale, or mismatched cache data is
DEGRADED. Effort is also observational; Federate does not invent an effort flag.
For offline startup, use scripts/fed_agent_check.py check --offline. Codex's
bundled catalog is reported as non-authoritative, so Federate keeps a
last-successful selection or omits flags for native account behavior on first
run. Claude and Hermes retain their normal non-network signals. Discovery
failures never erase durable state. Corrupt or unknown future schemas report
DEGRADED for non-overridden agents, and state writers refuse to overwrite
them.
FED_HERMES_CMD can launch a Hermes peer through your own SSH wrapper, while
fed_read.py hermes normally reads a local ${HERMES_HOME:-~/.hermes}/state.db.
For a remote peer, set:
export FED_HERMES_REMOTE_READ=ssh
export FED_HERMES_SSH_CMD="ssh -i ~/.ssh/hermes_key -o IdentitiesOnly=yes -o BatchMode=yes user@host"
export FED_HERMES_REMOTE_STATE_DB="/home/user/.hermes/state.db"When FED_HERMES_REMOTE_READ=ssh is set, fed_read.py hermes --nonce ...
pipes a stdlib-only Python reader to FED_HERMES_SSH_CMD, opens the remote
SQLite DB read-only, and returns the same structured extraction result as local
Hermes reads: top-and-bottom nonce anchoring, assistant text until the next user
turn, canonical window hash, and structured tool-event detection. Receipts use
source_kind: sqlite and a hermes+ssh://cmd-<sha256>/<db> source path. The
hash binds the expanded SSH argv that will actually execute, so env-var or
tilde changes alter the source identity. That source path can be re-extracted
during fed_cross.py verify without setting FED_HERMES_REMOTE_READ=ssh; the
hermes+ssh:// source itself selects remote mode, and verification succeeds
only when FED_HERMES_SSH_CMD expands to the same argv.
Federate does not manage SSH secrets. The SSH command is split without a shell,
and the nonce plus DB path are passed as remote Python argv. Keep
FED_HERMES_REMOTE_STATE_DB to the specific peer profile DB when the remote host
runs multiple Hermes profiles.
A peer CLI can boot into a blocking interstitial instead of a composer. The common Codex case is an update menu with "Update now" preselected; a blind Enter can trigger an upgrade instead of starting the round.
Run fed_ready.sh after fed_sessions.sh and before any first send:
scripts/fed_ready.sh fed-<ns>-codex-0 fed-<ns>-hermes-0It prints READY <session> only when a managed peer appears idle at a live
composer. It prints NOT_READY <session> ... and exits nonzero for unmanaged
or foreign sessions, busy panes, auth/trust prompts, timeout, or unclearable
menus. For the known Codex update menu it sends Down from "Update now" and
presses Enter only after the selected line is exactly plain "Skip"; unexpected
menu shapes receive no Enter. Set FED_NO_AUTO_SKIP=1 to detect the prompt
without touching it. FED_READY_TIMEOUT, FED_READY_POLL, and
FED_READY_CAPTURE_LINES tune the bounded poll.
The default busy detector is shared by fed_ready.sh, fed_sessions.sh, and
fed_wait.sh, and is intentionally limited to active-turn and interrupt
markers. Override FED_BUSY_RE when an agent TUI changes or a specialized
environment needs additional busy markers.
SKILL.md
agents/
openai.yaml Codex UI metadata and explicit-invocation policy
profiles/
llm_opa.min.txt managed default coordinator and peer operating profile
llm_opa.meta.json source commit, hash, and license metadata
model_policy.v1.json
versioned quality policy applied to agent-specific signals
scripts/
fed_agent_check.py
combine agent signals and maintain external successful-launch
and fingerprint-acknowledgment state
fed_sessions.sh create/reuse tmux sessions and print attach commands for
Claude, Codex, Hermes
fed_send.sh nonce-tag, bracketed-paste, verify, submit
fed_read.py read transcripts/state by nonce; optionally mint receipts
fed_cross.py generate/verify receipt-bound verbatim cross briefs
fed_round_check.py
verify every sent nonce is accounted for before synthesis
fed_ready.sh drive managed peer panes to a live composer; safely clear
known startup interstitials or report a blocker
fed_wait.sh wait until listed sessions appear idle
fed_update_check.sh
check/apply installed skill updates by recorded commit
fed_profile_check.py
check/apply managed operating-profile metadata and hash
install.sh install into Claude, Codex, and Hermes skill homes
.federate-install.json
generated install metadata: source, ref, commit, dirty flag, timestamp
- Transcript/state reads are the source of truth. Tmux is the visible runtime and liveness surface; tmux scrollback is not the reply source.
fed_read.py codexreturns final-answer blocks when Codex phase tags are present.fed_read.py --receipt-dir DIRwrites canonicalreply_<agent>.txtandreceipt_<agent>.jsonsidecars for successful non-empty nonce reads.fed_read.py --no-tool-window --require-no-tool-auditis the hard gate for yolo-preserving cross-show replies: the nonce window must contain no structured tool events, and the first non-empty reply line must be exactlyNO_TOOL_AUDIT: no tools used.fed_cross.py verify [--round N]re-extracts each receipt from the recorded source and reconstructs cross briefs byte-for-byte, so un-attributed edits fail.fed_round_check.py [--round N]compares a round'sround_manifest.jsonto itscross_manifest.jsonso a sent peer cannot silently disappear from synthesis.fed_read.py hermessearches${HERMES_HOME:-~/.hermes}/state.dband profile databases for the nonce.fed_read.py hermescan read a remote peer DB over SSH withFED_HERMES_REMOTE_READ=sshandFED_HERMES_SSH_CMD; remote receipts remain re-extractable through ahermes+ssh://cmd-<sha256>/<db>source path that binds the expanded SSH argv.fed_send.shinserts the nonce at the top and bottom of a brief. The top nonce anchors transcript/state reads; both markers must be visible in the composer before Enter is sent.fed_agent_check.pywrites model state outside the installed skill and marks a launch successful only after the coordinator records a nonce-bound reply.fed_send.shrefuses to paste into sessions that are not namespaced federate-managed peers unlessFED_SKIP_OWNER_CHECK=1is set for manual debugging.fed_send.shinjectsFED_PROFILE_FILEwhen set as a delimited FEDERATION PROFILE section after the top nonce of every brief. When unset, it injects the managed default profile fromprofiles/llm_opa.min.txtunlessFED_NO_DEFAULT_PROFILE=1is set. Bad profile paths and private-key-looking content fail before paste.- Token conservation helps most when peers produce compact, evidence-dense original answers. Do not post-process peer replies into compressed prose before cross-pollination; that can delete uncertainty, minority reports, safety qualifiers, and produce false convergence. The opposite is not blanket verbosity either. Ask for enough detail to preserve receipts, assumptions, confidence, and blocking deltas, then keep narrative short.
- A clean test pass is not federation sign-off for build work. The skill keeps the original rails: distinct owners for cross-checking artifacts, sealed expected values, coordinator-recomputed hashes, adversarial review, and operator gates for irreversible actions.
MIT. See LICENSE.