Skip to content

feat(fleet): federated multi-operator coordination with per-surface quota routing - #1103

Closed
adibirzu wants to merge 19 commits into
kunchenguid:mainfrom
adibirzu:federation
Closed

adibirzu wants to merge 19 commits into
kunchenguid:mainfrom
adibirzu:federation

Conversation

@adibirzu

@adibirzu adibirzu commented Jul 27, 2026 •

Copy link
Copy Markdown

Intent

Fleet add-on (PR #1103): federated multi-operator coordination + per-surface subscription-quota visibility with model->surface failover, made safe and usable from a bare clone (ownership + initialization guards, generic root prereq, use-case quickstart docs). Goal: pass the Require-no-mistakes gate and ship the branch through the sanctioned pipeline so PR #1103 carries the pipeline signature.

What Changed

  • Added a fleet add-on for federated multi-operator coordination: a shared-directory knowledge base with atomic claim/locking, scope routing, handoff, and operator lifecycle (register/heartbeat/leave/join), plus bare-clone safety via initialization and ownership guards, a reviewable idempotent root-prereq script, and a fm-fleet-wait.sh claim-wake loop (bin/fm-fleet.sh, bin/fm-fleet-lib.sh, scripts/fleet-root-prereq.sh).
  • Added per-surface subscription-quota visibility with model→surface failover and a quota floor for budget gating: authed Cursor and Copilot usage readers feed headroom into quota/models/pick/budget verbs, with an authed override hook and float-safe headroom comparisons (bin/quota-sources/, config/model-surfaces.json).
  • Added multi-account support: an account registry with cross-uid-safe resolution and three isolation methods, a quota-aware account pick, a --account spawn axis via a raw-launch shim with fail-closed API-key handling, and on-demand user-scoped prereq installers — all covered by seven test suites under tests/federation/ and documented in the fleet add-on, quickstart, and token-economy docs plus federation/multi-account operator skills.

Risk Assessment

✅ Low: All four actionable review findings from round 1 are correctly fixed at the recommended shared boundary with new guard tests covering each fix, no new defects were introduced by the fix commit, and the only remaining items are previously surfaced informational no-ops.

Testing

Ran all 7 hermetic federation test suites added by this change (78/78 assertions pass, exit 0 each), then manually exercised the product CLI end-to-end: the bare-clone first-run guard refuses with quickstart guidance, a two-operator fleet completes the queue/route/claim/handoff/view workflow, and the quota surface shows per-surface headroom with correct model->surface failover (including float compares and the budget floor exiting non-zero); the root prereq script was syntax-checked only since it needs sudo, and the worktree was left clean.

Evidence: Federation test suites output (7 suites, 78 assertions, all pass)
=== tests/federation/test_account_quota.sh ===
PASS: pick highest headroom (90 > 20)
PASS: quota-axi absent -> first registered (claude-high)
PASS: no quota provider -> first (pi-a)
PASS: tie -> first registered (claude-eqa)
PASS: single account picked directly
-----
ALL PASS
exit=0

=== tests/federation/test_accounts.sh ===
PASS: resolve returns per-account config_dir
PASS: unknown account fails
PASS: api-key resolve exposes key_file
PASS: validate accepts good account
PASS: unknown harness rejected
PASS: wrong env-for-harness rejected
PASS: foreign-home path refused
-----
ALL PASS
exit=0

=== tests/federation/test_fleet.sh ===
PASS: init creates KB
PASS: atomic claim: exactly one winner, one record
PASS: reap requeues stale claim
PASS: reap leaves in-flight alone
PASS: route: backend->adi, web->royce
PASS: route: offline owner -> overflow
PASS: handoff reassigns + logs event
PASS: view renders events
PASS: status renders header
PASS: safety: foreign home refused
PASS: safety: /opt shared dir allowed
-----
ALL PASS
exit=0

=== tests/federation/test_fleet_guards.sh ===
PASS: uninitialized fleet exits non-zero (was 0)
PASS: uninitialized fleet names the problem
PASS: no raw awk error
PASS: diagnostic says HOW the dir was chosen
PASS: default -> foreign fleet: 'status' refuses
PASS: default -> foreign fleet: 'status' leaks nothing
PASS: default -> foreign fleet: 'view' refuses
PASS: default -> foreign fleet: 'view' leaks nothing
PASS: route discloses no foreign operator
PASS: explicit FM_FLEET_DIR is honoured (no ownership check)
PASS: FM_FLEET_ACCEPT_DEFAULT=1 acknowledges the default
PASS: default is allowed when you ARE an operator in it
PASS: models is not blocked by a missing fleet
PASS: budget is not blocked by a missing fleet
PASS: wait refuses an uninitialized fleet loudly
PASS: default -> foreign fleet: wait refuses
PASS: default -> foreign fleet: wait leaks nothing
-----
ALL PASS (17)
exit=0

=== tests/federation/test_fleet_ops.sh ===
PASS: register writes an operator row
PASS: route finds a freshly-registered operator
PASS: register is idempotent (one row)
PASS: route: stale-heartbeat operator treated offline -> overflow
PASS: heartbeat refreshes seen -> operator online again
PASS: leave -> offline -> overflow
PASS: route: owner below quota floor -> overflow
PASS: route: owner above quota floor -> owner
PASS: budget_ok: above floor passes
PASS: budget_ok: below floor fails
PASS: register refuses a foreign home
PASS: wait --once: fresh claim wakes (exit 0 + id)
PASS: wait --once: no claim -> exit 1 (LLM stays idle)
PASS: wait --once: in-flight item is not a fresh wake
PASS: join: writes config/fleet-dir + registers self
PASS: join: idempotent (one row on rejoin)
-----
ALL PASS
exit=0

=== tests/federation/test_quota_surfaces.sh ===
PASS: pick grok fails over to cursor when grok drained
PASS: pick claude -> claude (has headroom)
PASS: pick kimi -> kimi (only surface; cline unmonitored)
PASS: pick unknown family is flagged
PASS: quota view includes cursor (custom source)
PASS: models view: grok reachable via cursor
PASS: cline is not a monitored surface
PASS: authed override: cursor headroom reads 55% from its reader
PASS: copilot custom source supersedes quota-axi auth_required row (88%)
PASS: models view: claude reachable via copilot
PASS: models view: gpt reachable via copilot
PASS: pick claude fails over to copilot when the claude pool is drained
-----
ALL PASS (12)
exit=0

=== tests/federation/test_spawn_account.sh ===
PASS: compose config-dir-env (claude)
PASS: compose folds model+effort
PASS: compose codex (CODEX_HOME)
PASS: compose config-dir-flag (cline)
PASS: api-key compose refused (no key on argv)
PASS: unknown account refused
PASS: wrapper passes (id, dir, composed-launch) to fm-spawn
PASS: wrapper fail-closed on api-key account
PASS: apply_env exports config-dir-env in caller shell
PASS: apply_env sets argv suffix for flag method
-----
ALL PASS
exit=0
Evidence: Fleet CLI e2e transcript: bare-clone guard + multi-operator workflow

$ bin/fm-fleet.sh status # bare clone, nothing configured fm-fleet: no initialized fleet at .../nonexistent (nothing configured, so the built-in default ... was used) the directory does not exist. Pick one: solo / trying it out FM_FLEET_DIR=~/.firstmate-fleet bin/fm-fleet.sh init shared, multi-operator sudo bash scripts/fleet-root-prereq.sh already have one export FM_FLEET_DIR=/path/to/fleet (exit 1) $ bin/fm-fleet.sh init && register adi + royce && queue FL-42 backend $ bin/fm-fleet.sh route backend adi $ bin/fm-fleet.sh claim FL-42 adi && bin/fm-fleet.sh handoff FL-42 royce $ bin/fm-fleet.sh status operator claimed in-flight last-event adi 0 0 2026-07-28T07:25:48Z royce 1 0 2026-07-28T07:25:48Z

$ # --- Bare-clone first run: no fleet configured anywhere ---
$ bin/fm-fleet.sh status
fm-fleet: no initialized fleet at /tmp/tmp.bpuGXjAdYG/nonexistent
  (nothing configured, so the built-in default /tmp/tmp.bpuGXjAdYG/nonexistent was used)
  the directory does not exist.

Pick one:
  solo / trying it out   FM_FLEET_DIR=~/.firstmate-fleet bin/fm-fleet.sh init
  shared, multi-operator sudo bash scripts/fleet-root-prereq.sh   # then: bin/fm-fleet.sh init
  already have one       export FM_FLEET_DIR=/path/to/fleet   (or write it to /tmp/tmp.bpuGXjAdYG/fmhome/config/fleet-dir)

See docs/fleet-quickstart.md.
(exit 1)

$ # --- Operator workflow: init, register two operators, queue + claim + handoff ---
$ export FM_FLEET_DIR=...   # explicit shared dir
$ bin/fm-fleet.sh init
fleet initialized at /tmp/tmp.bpuGXjAdYG/fleet
$ bin/fm-fleet.sh register adi backend,infra /home/adi/.no-mistakes/worktrees/edd867c2e6ae/01KYKS5PJ8V1N4T10ZZMEVZZC4 claude-default
$ bin/fm-fleet.sh register royce web /home/adi/.no-mistakes/worktrees/edd867c2e6ae/01KYKS5PJ8V1N4T10ZZMEVZZC4 claude-default
$ bin/fm-fleet.sh queue FL-42 backend "wire the quota dashboard"
$ bin/fm-fleet.sh route backend
adi
$ bin/fm-fleet.sh claim FL-42 adi
$ bin/fm-fleet.sh handoff FL-42 royce
$ bin/fm-fleet.sh status
operator            claimed  in-flight  last-event
adi                  0        0          2026-07-28T07:25:48Z
royce                1        0          2026-07-28T07:25:48Z
$ bin/fm-fleet.sh view
2026-07-28T07:25:47Z adi        register -        scope:backend,infra
2026-07-28T07:25:48Z royce      register -        scope:web
2026-07-28T07:25:48Z -          queue    FL-42    scope:backend
2026-07-28T07:25:48Z adi        claim    FL-42    
2026-07-28T07:25:48Z royce      handoff  FL-42    assigned
Evidence: Quota surface + model->surface failover transcript

$ bin/fm-fleet.sh quota SURFACE HEADROOM STATUS SOURCE NOTE grok 2% fresh oauth observable claude 3.5% fresh oauth observable codex 90% fresh cli-rpc observable kimi 60% fresh oauth observable copilot 88% logged_in custom gh api reader (stub) cursor 80% logged_in custom authed Connect reader (stub) $ bin/fm-fleet.sh models grok grok: fresh 2% | cursor: logged_in 80% claude claude: fresh 3.5% | copilot: logged_in 88% | cursor: logged_in 80% $ bin/fm-fleet.sh pick grok # drained -> failover cursor $ bin/fm-fleet.sh pick claude # 3.5% float below floor -> failover copilot $ bin/fm-fleet.sh budget below floor (< 5%) (exit 1)

$ # --- Per-surface quota visibility (grok nearly drained, claude pool at 3.5%) ---
$ bin/fm-fleet.sh quota
SURFACE  HEADROOM  STATUS     SOURCE   NOTE
grok     2%        fresh      oauth    observable
claude   3.5%      fresh      oauth    observable
codex    90%       fresh      cli-rpc  observable
kimi     60%       fresh      oauth    observable
copilot  88%       logged_in  custom   gh api reader (stub)
cursor   80%       logged_in  custom   authed Connect reader (stub)

$ # --- Model -> surface reachability ---
$ bin/fm-fleet.sh models
MODEL   SURFACES (pool: status headroom)
grok    grok: fresh 2%  |  cursor: logged_in 80%
kimi    kimi: fresh 60%
claude  claude: fresh 3.5%  |  copilot: logged_in 88%  |  cursor: logged_in 80%
gpt     codex: fresh 90%  |  copilot: logged_in 88%  |  cursor: logged_in 80%

$ # --- Failover picks: drained primary surfaces fall over ---
$ bin/fm-fleet.sh pick grok      # grok oauth at 2% -> cursor (80%)
cursor
$ bin/fm-fleet.sh pick claude    # claude pool at 3.5% (float) -> copilot (88%)
copilot
$ bin/fm-fleet.sh pick kimi
kimi

$ bin/fm-fleet.sh budget; echo "(exit $?)"
below floor (< 5%)
(exit 1)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 6 issues found → auto-fixed ✅
  • ⚠️ bin/fm-fleet-wait.sh:23 - fm-fleet-wait.sh bypasses the bare-clone guards that commit 6150d6a introduced as the durable fix: it resolves DIR via fm_fleet_dir but never calls fm_fleet_assert_initialized or fm_fleet_assert_owned. Reachable failures: (a) bare clone with nothing configured -> fresh_claims greps a nonexistent backlog.md with 2>/dev/null and the script silently waits forever (or --once exits 1 indistinguishable from 'no claim'), the exact silent-failure mode the guards were written to eliminate; (b) on a shared host where the built-in default /opt/agents/fleet is another team's group-writable fleet, a bare clone running fm-fleet-wait.sh reads that team's backlog and, via the heartbeat path (fm_fleet_heartbeat -> fm_fleet_lock), creates/locks files inside their dir without the membership opt-in that fm_fleet_assert_owned enforces for fm-fleet.sh verbs. Recommend enforcing the guard at the shared boundary (e.g. a helper called right after fm_fleet_dir resolution, used by fm-fleet.sh, fm-fleet-wait.sh, and future entry points) instead of only in fm-fleet.sh's case statement.
  • ⚠️ bin/fm-fleet-lib.sh:472 - Headroom comparisons use integer-only shell tests and break on fractional percentages. fm_fleet_budget_ok does [ "$q" -ge "$floor" ] 2>/dev/null on the jq min of quota-axi percentRemaining; if quota-axi reports a float (e.g. 90.5), the test errors (suppressed) and the function returns failure — the operator is held back despite ample headroom, inverting the documented fail-open contract. Same pattern in fm_fleet_pick_surface ([ "$h" -ge "$floor" ], surface silently treated as unobservable) and fm_account_pick in fm-accounts-lib.sh:164 ([ "$hr" -gt "$best_hr" ], unsuppressed bash error on stderr and quota-aware pick silently degrades to first-registered). The awk paths (fm_fleet_route's (q+0)<floor) already handle floats; convert these three shell tests to awk numeric comparisons for consistency.
  • ⚠️ bin/fm-fleet.sh:33 - The budget verb is misclassified in the guard dispatch: fm_fleet_budget_ok only calls quota-axi and never reads the fleet dir, making it surface-local exactly like quota/models/pick, but it falls into the * branch and therefore requires an initialized AND owned fleet. On a bare clone (Tier A usage, no fleet) fm-fleet.sh budget fails with 'no initialized fleet' even though nothing it does needs one. Add budget to the surface-local exemption list on line 29. Note the guard tests exercise status/view/route/models but not budget, so this gap is untested.
  • ℹ️ scripts/fleet-root-prereq.sh:46 - chmod -R 2775 &#34;$DIR&#34; applies the setgid and execute bits to regular files as well as directories. On a re-run over an existing KB (the script advertises idempotent re-runs), operators.md, backlog.md, and events.log become mode 2775 — setgid executables, which on Linux can also flag mandatory locking semantics. Use find &#34;$DIR&#34; -type d -exec chmod 2775 {} + for directories and chmod -R g+rw (no exec/setgid) for files.
  • ℹ️ bin/fm-fleet-lib.sh:40 - fm_fleet_dir and fm_fleet_dir_source duplicate the resolution logic and can disagree: if $FM_HOME/config/fleet-dir exists but its first line is empty, fm_fleet_dir falls through to the built-in default while fm_fleet_dir_source still reports 'config', so fm_fleet_assert_owned returns early and the ownership guard is silently skipped for what is actually the default shared dir. Low likelihood (join always writes a non-empty path), but the two functions must stay in lockstep; deriving the source alongside the dir in one place (e.g. emitting 'dir<TAB>source') would remove the divergence risk.
  • ℹ️ bin/fm-spawn-acct.sh:32 - Argument parsing in fm-spawn-acct.sh assigns any unrecognized token to the positional slots while fewer than two are filled, so a passthrough flag appearing before the two positionals (e.g. fm-spawn-acct.sh --scout T-1 /proj --account a) is silently captured as task-id and the real project-dir is demoted to a passthrough arg, producing a garbled fm-spawn invocation. The documented usage puts flags last, so this only bites out-of-order invocations; rejecting an unknown --* token before both positionals are filled would fail loudly instead.

🔧 Fix: fix wait guard bypass, float headroom compares, budget exemption, prereq chmod
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • bash tests/federation/test_account_quota.sh — quota-aware account pick (headroom, tie, absent-provider guards): all pass
  • bash tests/federation/test_accounts.sh — account registry resolution/validation, foreign-home refusal: all pass
  • bash tests/federation/test_fleet.sh — init, atomic no-overlap claim race, TTL reap, scope routing, handoff, view/status, cross-uid safety: all pass
  • bash tests/federation/test_fleet_guards.sh — bare-clone resolution guards: uninitialized-fleet refusal, foreign-fleet non-disclosure (status/view/route/wait), explicit-dir and membership acceptance, budget/models exemption: all 17 pass
  • bash tests/federation/test_fleet_ops.sh — operator lifecycle (register/heartbeat/leave/join), quota-floor routing, wait --once wake semantics: all pass
  • bash tests/federation/test_quota_surfaces.sh — per-surface quota view, model->surface failover, authed override hook, copilot custom source superseding auth_required: all 12 pass
  • bash tests/federation/test_spawn_account.sh — --account spawn composition, api-key fail-closed shim: all pass
  • Manual e2e transcript: bare-clone fm-fleet.sh status refuses with actionable diagnostic; then init → register x2 → queue → route → claim → handoff → status/view against a temp fleet dir
  • Manual e2e transcript: fm-fleet.sh quota, models, pick grok|claude|kimi (drained-primary failover incl. float 3.5% headroom), budget exiting 1 below the 5% floor, with stubbed quota-axi and custom cursor/copilot readers
  • bash -n scripts/fleet-root-prereq.sh plus source inspection confirming the review-fix chmod (setgid 2775 dirs, g+rw files); not executed because it requires root and would modify system state outside the worktree
⚠️ **Document** - 1 info
  • ℹ️ docs/fleet-addon.md:74 - Out-of-scope consolidation follow-up: the CLI auth-isolation matrix is stated in full in both docs/fleet-addon.md (Part B) and docs/fleet-quickstart.md (Tier B), and fleet-addon.md's 'Install (drop-in overlay)' and 'Packaging options' sections are pre-merge delivery narrative now that the add-on ships in-repo via PR feat(fleet): federated multi-operator coordination with per-surface quota routing #1103. A follow-up should make one surface the matrix owner (reduce the other to a pointer) and trim the overlay/packaging prose from the maintainer reference.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@adibirzu

Copy link
Copy Markdown
Author

Updated: the federation branch now also includes the operator lifecycle (register/heartbeat/leave/join), quota-aware + heartbeat-freshness routing (operators self-publish quota-axi headroom; stale/low-headroom operators are skipped — no cross-user auth), a bash wake-on-claim watcher (fm-fleet-wait.sh — the token-economy core: an operator's LLM stays idle until it has a fresh claim), and a reviewable idempotent root-prereq script. New tests: tests/federation/test_fleet_ops.sh (16 checks, all green). Docs: docs/fleet-token-economy.md. Default single-operator behavior is unchanged; the operators.md schema addition (seen/quota) is backward-compatible with 5-column rows.

@adibirzu

Copy link
Copy Markdown
Author

Update — usable from a bare clone, plus a use-case quickstart

I tested this the way a new user would: cloned the branch into a scratch dir with a
clean HOME and nothing configured, then ran the commands the docs suggest. Four
things were wrong, one of them a data leak. All are fixed in this push.

1. A clone that configured nothing silently attached to another team's fleet.
The built-in default fleet dir is a conventional shared path. Where it already
exists — group-writable and world-readable by design — status / route / view
listed someone else's operators and dumped their entire event log: task ids,
scopes, who claimed what. No prompt, no warning.

Membership is now the opt-in signal. Reaching a fleet you are not an operator of
via the default is refused. An explicit FM_FLEET_DIR or config/fleet-dir is
always honoured, FM_FLEET_ACCEPT_DEFAULT=1 acknowledges the default, and
register/heartbeat/leave are exempt because they are how you become an
operator (and already need group write access).

2. An uninitialized dir surfaced as a raw internal error that looked like success.
awk: fatal: cannot open .../operators.md with exit 0. Now a diagnostic naming
the directory, how it was chosen, and the fix — with exit 1.

3. scripts/fleet-root-prereq.sh defaulted to my own usernames. Run on any
other host it would try to enrol accounts that mean nothing there. Now defaults to
$SUDO_USER and refuses when it cannot infer one.

4. config/accounts.json.example hardcoded my home in all 7 entries. Paths are
used literally — no ~/$HOME expansion — so a copied template pointed into a
foreign home. Now /home/YOUR-USER, with the constraint documented.

docs/fleet-quickstart.md

The add-on had three reference docs but no entry point, and nothing in README or
AGENTS.md mentioned it existed. The quickstart is organised by use case, because
the two halves are independent and most people want only the first:

Use case Root? Setup
A See remaining budget across every AI subscription you own, and fail work over to whichever pool still has headroom no ~2 min
B Several accounts for one person no ~5 min
C Several people sharing one host once ~15 min

Tier A needs no fleet, no root and no shared directory — it works in a bare clone.
It also now covers GitHub Copilot: quota-axi ships a native copilot provider
but probes only ~/.config/github-copilot/apps.json, the old IDE-plugin credential
path, so a standalone Copilot CLI login is invisible to it and the row sits at
auth_required forever. bin/quota-sources/copilot.sh supersedes that row using
the CLI's own token store, and bin/quota-copilot-usage.sh reads real numbers from
copilot_internal/user — no browser cookie and no second login, unlike Cursor.

Troubleshooting covers the traps that actually cost time here: stale group
credentials in a long-lived tmux or systemd --user manager (id tells you the
truth, /proc/<pid>/status tells you the reality), XDG_RUNTIME_DIR in
non-login sessions, and heartbeats being mandatory for routing to work at all.

Note on scope

This PR now touches README.md (two bullets in the documentation list) in addition
to .gitignore. If you would rather keep the diff off README.md entirely, say so
and I will move the pointer to a follow-up — but without it the feature is
undiscoverable.

tests/federation/test_fleet_guards.sh adds 13 checks over directory resolution,
both guards, and non-disclosure. Worth noting the guards were briefly inert:
DIR=$(fm_fleet_dir) runs in a subshell, so a global set inside it never reached
the caller. Provenance is a function now, and the tests pin it.

Federation + adapter suites: 9/9 green. Rebased on current main.

adibirzu added 18 commits July 28, 2026 07:14
…edit); secure api-key exec shim; fix subshell-export bug (live-verified isolation)
…under isolation; tie/absent/no-provider guards)
…detect default; pi=system-managed detect-only; curl|sh gated)
…en economy

Makes federated mode actually usable per-operator, in-sync, and token-cheap.

- fm-fleet-lib.sh: register/heartbeat/leave (self-onboard, upsert, own-home-only);
  operators.md gains seen+quota columns; route is now freshness-aware (stale
  heartbeat => offline, TTL FM_FLEET_HEARTBEAT_TTL) AND quota-aware (skip operators
  below FM_FLEET_QUOTA_MIN, headroom self-published on heartbeat — no cross-user
  auth); fm_fleet_quota_now + fm_fleet_budget_ok. Heartbeat is a bash file-write,
  never git-committed (no audit-log bloat).
- fm-fleet.sh: register|heartbeat|leave|budget verbs.
- fm-fleet-wait.sh: bash wake-on-claim watcher (0 LLM tokens) — a primary blocks
  here and its LLM wakes only when it has a fresh status:claimed item; heartbeats
  while waiting.
- fm-fleet-join.sh: idempotent self-onboarding (verify shared access -> point
  config/fleet-dir -> register); cross-uid-safe.
- docs/fleet-token-economy.md + federation SKILL: the event-driven, bash-coordinated
  model and its knobs.
- tests/federation/test_fleet_ops.sh: 16 checks (all green); existing test_fleet.sh
  stays green (schema/route change is backward-compatible with 5-column rows).
…eged step)

scripts/fleet-root-prereq.sh creates the agents group + group-writable
/opt/agents/fleet (2775 setgid) + operator group membership. Idempotent, additive,
with reverse steps. Run once: sudo bash scripts/fleet-root-prereq.sh.
… authed override hook

- fm-fleet.sh quota: every LLM/CLI/app pool + observability (quota-axi + pluggable bin/quota-sources/*)
- fm-fleet.sh models: model family -> serving surfaces with live status
- fm-fleet.sh pick <family>: failover selector (grok from grok|cursor; kimi3 via cline)
- config/model-surfaces.json: model -> ordered pools
- bin/quota-sources/{cline,cursor}.sh: surfaces quota-axi can't see; authed numbers via
  config/quota-overrides.json escape hatch (operator-supplied reader supersedes blind row)
- cursor usage is browser-session-gated (CLI bearer can't read it): documented, blind fail-open default
- tests/federation/test_quota_surfaces.sh: 8 hermetic checks
…room)

Reads a STABLE CLINE_API_KEY (preferred) or cline's own ~1h WorkOS session token from
providers.json and queries GET api.cline.bot/api/v1/users/<uid>/balance; maps credit
balance -> routability headroom (credits>0 => 100). Blind when the token is stale (we do
NOT refresh out-of-band: the refresh token is single-use and would break cline's login).
Token via 0600 header file, never argv. Wire via config/quota-overrides.json .cline.
…e -> headroom)

Cursor usage IS readable with the CLI's OWN access token (no browser cookie) via the
native Connect RPC POST api2.cursor.sh/aiserver.v1.DashboardService/GetCurrentPeriodUsage
(Content-Type: application/json, Connect-Protocol-Version: 1, x-cursor-client-* headers).
headroom = 100 - planUsage.totalPercentUsed. Verified live: 71%. Token via 0600 header
file, never argv. Wire via config/quota-overrides.json .cursor.
… reader note fix

cline balance is locked behind an internal WS-hub credential (WorkOS token 401s on every
REST/header variant), so it can't be monitored. Remove its quota-source + reader + model-
surfaces entries; cline stays a usable crewmate harness (fm-spawn adapter untouched) but is
out of quota routing. Cursor stays live (71%). Tests updated (cline-free), all green.
Adds GitHub Copilot CLI as a live-monitored quota surface and repairs the
cursor reader, which had gone blind.

copilot:
- quota-axi ships a native `copilot` provider but probes only the OLD IDE
  credential path (~/.config/github-copilot/apps.json), so a standalone
  Copilot CLI login is invisible to it and the row sits at auth_required
  forever. bin/quota-sources/copilot.sh supersedes that row.
- bin/quota-copilot-usage.sh reads the CLI's own token from
  ~/.copilot/config.json (.copilotTokens; JSON-with-//-comments) and calls
  GET api.github.com/copilot_internal/user, taking the MIN percent_remaining
  across metered quota_snapshots buckets (chat/completions report
  unlimited=true; premium_interactions is the metered one). No browser
  cookie and no second login required — unlike cursor and cline.
- Token via 0600 header file, never argv/stdout; any failure exits 0 with no
  output => blind / fail-open, matching the cursor contract.
- model-surfaces: claude -> [claude, copilot, cursor], gpt -> [codex,
  copilot, cursor], so both families can now fail over into Copilot.

cursor fix:
- `cursor-agent about` emits ANSI SGR codes around the version string. The
  unstripped ESC[22m landed in x-cursor-client-version, and the usage RPC
  answered 400 Bad Request -> reader printed nothing -> surface blind. Strip
  CSI sequences and restrict to version-safe characters. Live again.

tests: test_quota_surfaces 8 -> 12 checks (custom source supersedes the
auth_required row; claude/gpt reach copilot in the models view; pick claude
fails over to copilot when the claude pool is drained). Federation
regression 49/49 green.

Verified live: copilot 71%, cursor 69% (restored).

(cherry picked from commit 9e4ba3a)
Ran the actual fresh-clone experience instead of reading the code. Four defects,
one of them a data leak.

1. A clone that configured NOTHING silently attached to the built-in default fleet
   dir. Where that path already exists (a conventional, group-writable, world-
   readable location), `status`/`route`/`view` listed another team's operators and
   dumped their entire event log — task ids, scopes, who claimed what. Membership
   is now the opt-in signal: reaching a fleet you are not an operator of *via the
   default* is refused. An explicit FM_FLEET_DIR or config/fleet-dir is always
   honoured, and FM_FLEET_ACCEPT_DEFAULT=1 acknowledges the default. register/
   heartbeat/leave are exempt (they are how you become an operator, and already
   need group write access).

2. An uninitialized dir surfaced as `awk: fatal: cannot open .../operators.md`
   with exit 0 — a raw internal error that also looked like success to a caller.
   Now a diagnostic naming the dir, HOW it was chosen, and the fix; exit 1.

3. scripts/fleet-root-prereq.sh defaulted to the maintainer's own usernames, so
   running it on any other host tried to enrol accounts that mean nothing there.
   Defaults to ${SUDO_USER} and refuses when it cannot infer one.

4. config/accounts.json.example hardcoded /home/adi in all 7 entries; paths are
   used literally (no ~ or $HOME expansion), so a copied template silently pointed
   into a foreign home. Now /home/YOUR-USER with that constraint documented.

Also: actionable messages when quota-axi is absent vs present-but-unauthenticated,
and the federation skill no longer presents one maintainer's home name as canonical.

docs/fleet-quickstart.md is the missing entry point, organised by use case:
  A  see remaining budget across every AI subscription + model->surface failover
     (no root, no fleet, works in a bare clone)
  B  several accounts for one person
  C  several people on one host
with requirements per tier, fleet-dir resolution, and troubleshooting for the
traps that cost real time here (stale group credentials in a long-lived tmux or
systemd --user manager; XDG_RUNTIME_DIR; heartbeats being mandatory for routing).
Linked from README so the feature is discoverable at all — previously nothing in
README or AGENTS mentioned it.

tests/federation/test_fleet_guards.sh: 13 checks over dir resolution, both guards,
and non-disclosure. Note the guards were briefly inert: `DIR=$(fm_fleet_dir)` runs
in a subshell, so a global set inside it never reached the caller — the provenance
is a function now, and the tests pin it.

Federation + adapter suites: 9/9 green.

(cherry picked from commit 4279c65)
…add-on

SC2034 in fm-account-env.sh: FM_ACCT_ARGV_SUFFIX is read by fm-account-exec.sh
after sourcing — annotated with the repo's existing disable convention, placed
before the case statement (a directive on an individual case branch is SC1124).
SC2015 A-&&-B-||-C in the two column|cat table fallbacks and the budget verb —
rewritten as explicit if/else.

Found by running bin/fm-lint.sh locally with the pinned shellcheck; these files
predate CI running on the fork PRs (first-time-fork approval), so upstream lint
had never seen them.

(cherry picked from commit 867e6e2)
@adibirzu adibirzu changed the title feat(fleet): federated multi-operator mode + per-spawn multi-account axis (additive) feat(fleet): federated multi-operator coordination with per-surface quota routing Jul 28, 2026
The Repo invariants CI job requires zero tracked files under config/.
Move the three shipped fleet files to the docs/examples/ precedent
(accounts.json, quota-overrides.json, model-surfaces.json), teach
fm-fleet-lib.sh to fall back from the gitignored personal
config/model-surfaces.json to the shipped default so a bare clone still
routes, gitignore the personal override, and update the docs, skills,
tests, and documentation-audiences inventory references.

Verified locally: invariant ls-files empty, symlinks intact, fm-lint,
fm-doc-audience-check, coverage guard, and all 7 federation tests pass.
@adibirzu

Copy link
Copy Markdown
Author

Note for reviewers: #1187 supersedes this PR. That branch was stacked on this one, so it contains all 35 files here plus the quota-pace work (8 further files). Merging #1187 alone loses nothing from this PR; alternatively merge this one first and the CI monitor will rebase #1187 down to just its delta. Sorry for the duplication — the stacking was my error.

@adibirzu

Copy link
Copy Markdown
Author

Closing as superseded by #1217, which contains this PR in full (verified by file-set comparison: 0 files here are absent from #1217). Consolidating so there is only one PR to review — no content is lost. Reopen if you'd prefer the smaller unit.

@adibirzu adibirzu closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant