Docs

Supervise an agent fleet from one board

FleetView folds what the shipped producers already emit into one page: live agent VMs, merged pull requests by who started them, cost across the fleet, the tools and MCP servers each machine carries, and the fleet controls. It captures nothing new, and every control on it is dormant until you arm its flag.

For: the architect or lead watching several machines run coding agents, and the operator who decides when a fleet control gets armed

What it does, and why it helps

Every distinct device in the presence-projected active-session snapshot is one agent VM, and a VM is live when one of its sessions is fresh inside the presence window. The per-repo autonomous-PR metric is summed across every tracked repo into merge-ready pull requests by initiator, cost across the fleet, and lessons applied. Below the rollup sit the per-machine sections: the AI tools this machine runs, the MCP servers it has configured, what each vendor's own admin API reports, the loop-discipline and trust rollups, the agent registry, and the fleet controls.

The honesty rules live in the fold rather than the copy. A scope with no priced spend reads n/a, never a fabricated zero. The agent share is over classified pull requests only, and low-confidence and unclassified work stays visible in its own excluded bucket. A discipline with no signal in any repo reads unknown. Every fleet control computes a verdict and blocks nothing until an operator sets its flag in the data directory's .env file and restarts, so the Activation console can show what each one would have done while it is still off.

The pain. Agent work is spread over machines nobody watches all day. Producing a fleet number is easy; knowing which part of that number was measured, and which part was filled in, is the part that gets skipped.

The point of view. A fleet number you cannot check is worse than no number. Name the denominator, leave the gap as a gap, and let an operator see what a control would have blocked before deciding to arm it.

What gets easier. The weekly read. One page carries live VMs, agent share, cost per autonomous pull request, lessons applied, what each machine has installed and talks to, and which controls are dormant, with the excluded buckets printed beside the totals.

When it helps. Several machines run coding agents against repos this install tracks, and someone has to answer what the fleet shipped this window, what it cost, what is installed where, and what would happen if a control were armed.

Its limits. It is a local desktop tab and it folds, so a machine that never syncs its presence and capture is not on the board. It merges nothing and posts no check run: an ingest or merge caller has to ask POST /api/fleet/caps/check or POST /api/fleet/control/check and honor the verdict. Partner controls are recorded, never run. The one place a fleet gate refuses a real run inside this repo is the isolated local executor (lib/fleet/preflight.mjs).

Understand it in 30 seconds

30.1 s, captions on. Narration: Microsoft Zira Desktop (provisional voice; an approved narration source is pending).Transcript
Read the narration
  1. 0:00 Agents run on machines nobody watches.
  2. 0:03 The board still shows a number.
  3. 0:06 FleetView folds what already shipped.
  4. 0:08 It captures nothing new and fills no gap with zero.
  5. 0:13 A run nobody priced reads not available.
  6. 0:16 Unclassified work keeps its own bucket, and dormant controls show what they would have blocked.
  7. 0:23 Read the gaps, then arm a control.
  8. 0:25 FleetView never flips a flag.

Synthetic example. Read the guide

Where to find it

Where to find it

  • Desktop: localhost:4000, then AI Security in the sidebar, then FleetView under All tools, in the MCP and tool inventory group.
  • Hosted: desktop only.
  • Keyboard: ⌘ K, then type “FleetView”.

When to use it

The window read across every machine

Situation. Agents run on three machines against five tracked repos. Nobody has armed a control, and the window is the tab default of 90 days.

What you do. Open FleetView, read Fleet at a glance, then Merge-ready PRs by initiator and Recent fleet pull requests.

What you see. Six tiles: Live agent VMs, Agent share, Cost / autonomous PR, Cost across VMs, Lessons applied, Lessons that worked. The bars carry agent, human and mixed, with low-confidence and unclassified beneath them as their own excluded buckets. The pull request table lists repo, number, title, band, cost and merged date, newest first.

What it establishes. What the fleet merged, by whom, and which part of it is measured. A cost tile reads n/a when no agent pull request priced, and Lessons that worked reads not yet measurable until a lesson has run history on both sides of its first application. A measured drop in defect rate is a measurement, not a promise that the defect cannot come back.

What this machine runs, and what it talks to

Situation. A teammate reports an agent you have never configured, and nobody is sure which MCP servers are wired on the build machine.

What you do. Read AI tools in use and MCP servers. Turn on the process scan only if the adapter registry and the captured sessions leave a gap.

What you see. The tool table shows every registry adapter with Tool, Installed?, Detected at, Last seen and Sessions (30d); a row can read yes (sessions only) when the probe missed the install but sessions prove it. The MCP table shows Server, Source tool, Transport, Command / URL, First seen, Last seen and a Drift column that reads drifted or stable. With no config found the section says No MCP server configs detected on this machine.

What it establishes. A per-machine inventory from install paths, config files and captured sessions. No MCP server is executed, the probe calls an existence check and never reads inside a store, and the drift hash covers transport, command, args summary, URL and env var names, so a changed env var value shows nowhere.

Look before you arm a control

Situation. REPOOPS_FLEET_KILL and REPOOPS_FLEET_CAPS are unset, and someone asks what arming them would have cost the team last quarter.

What you do. Read the Activation console: four control cards with their real flag state, then the shadow card beneath them.

What you see. Each card reads armed or dormant beside its flag name, and a dormant card says to set the variable and restart, per the runbook. The shadow card is headed Shadow run over N recent merges, blocks nothing, with rows for attestation would emit, four-eyes would block, kill-switch and policy gate would block, and budget caps would block (fleet), which reads n/a (unpriced) when nothing priced. Blocking rows name the pull request numbers and the rule that fired.

What it establishes. A count of what each control would have stopped, read from the same evaluators the live gates use. The console points at docs/runbooks/cna-phase2-activation.md and flips no flag; arming stays an .env edit plus a restart.

Before you start

Supported versions
RepoOps desktop v0.3.1, the release this guide was read against. FleetView is a desktop tab and needs the app running with at least one tracked repo; the rollup is empty until a tracked repo has captured sessions.
Where it runs
Local. The tab is public/fleetview.html in the desktop app. The hosted sidebar carries a pointer row at repoops.ai/team/fleetview that routes to Telemetry and the Accountability ledger for the team-side numbers; the live per-machine run telemetry stays on the desktop. Two per-machine inventories can be published to a hosted team page, and nothing else on this tab is.
Permissions
Reads need nothing beyond the local app. Three writes take the operator-write gate (checkOperatorWrite in lib/local-origin.mjs): saving a budget cap, saving the kill-switch or policy config, and declaring an agent. Same-origin always, plus the x-repoops-operator-confirm header where REPOOPS_OPERATOR_CONFIRM_SECRET is set; on a non-loopback bind with no secret the write returns 501 naming the variable. The Operator confirmation field appears on the tab only when the machine asks for one. Engaging a halt, recording a break-glass row and lifting a guardian hold each require a who and a why.
Connections
None for the rollup itself. Live cross-VM presence needs a bound device with org sharing on; without it the tab polls and the badge reads poll. Vendor-reported usage needs CURSOR_ADMIN_KEY, or COPILOT_METRICS_TOKEN together with COPILOT_METRICS_ORG, in the data directory's .env. Publishing an inventory needs the device bound and cloud sync on.
Plan
No plan gate on the tab. The pricing capability map (CAPABILITY_MAP in website/lib/pricing-tiers.ts) has no FleetView row. Seeing a published inventory on the hosted team page follows the hosted dashboard tiers.

Configure it

  1. Open the tab and read the refresh badge.

    FleetView polls every 15 seconds and pauses while the tab is hidden. The badge reads live when the server reported a team presence room and the page opened the embedded relay, and poll otherwise, which is the documented fallback. Refresh forces a read.

  2. Decide what this machine publishes.

    Settings, the card What this machine publishes to your team, has a Tool inventory row and an MCP inventory row. Both are opt-in and off by default, and both also need the device bound with cloud sync on. Leave them off and FleetView stays entirely local.

  3. Turn on the process scan only if you need it.

    Set REPOOPS_PROCESS_SCAN=1 in the data directory's .env to add running-process evidence for agents no adapter covers. The value must be exactly 1. One bounded listing runs per collect (tasklist on Windows, ps elsewhere), process names only, and an unmatched name is discarded unrecorded.

  4. Add vendor admin keys if you want the vendor's own numbers.

    CURSOR_ADMIN_KEY pulls Cursor daily usage; COPILOT_METRICS_TOKEN with COPILOT_METRICS_ORG pulls Copilot seats and org metrics. With neither configured the pull makes zero outbound requests. The schedule is every 12 hours unless REPOOPS_VENDOR_USAGE_PULL_HOURS says otherwise, and Pull now runs one immediately.

  5. Set a ceiling per scope before you think about arming caps.

    Each of the repo, team and fleet cards carries a cap box, a mode of off, warn or block, and Save cap. An empty box means no ceiling, which is not the same as zero. Lifting a crossed cap uses Override this block, which refuses without a name and a reason because the row is audited.

  6. Read the shadow run, then arm a control in the .env and restart.

    The four controls are REPOOPS_ATTESTATION (armed by default since 2026-09-08; set 0 to disarm), REPOOPS_FOUR_EYES_GATE, REPOOPS_FLEET_CAPS and REPOOPS_FLEET_KILL. The last three are dormant unless set to 1, and while dormant each computes its verdict and blocks nothing. The console never arms anything.

  7. Declare who owns each agent.

    Agent registry, the Declare or retire an agent disclosure: owner, team, pinned model and lifecycle attach to one agent identity and persist per account. Ownership stays honest-unknown until declared, and retiring needs a who and a why.

SettingWhereA sensible choiceWhy it matters
REPOOPS_FLEET_CAPSthe data directory's .envunset while you read the shadow runUnset, a crossed cap reads Would block (set REPOOPS_FLEET_CAPS=1 to enforce) and allow stays true. Set to 1, a blocking cap refuses the isolated loop runs RepoOps starts for that scope.
REPOOPS_FLEET_KILLthe data directory's .envunset until a halt is something you would useUnset, an engaged switch or a firing rule records the reason that fleet control is OFF and the finding is advisory. Set to 1, the gate verdict refuses.
REPOOPS_FOUR_EYES_GATEthe data directory's .env1 to enforce the author-is-not-approver gateUnset, the verdict computes and downgrades to neutral.
REPOOPS_ATTESTATIONthe data directory's .envleave it alone (armed by default)The one activation-console control that ships armed. Setting 0 returns to an honest signed:false everywhere.
caps per scope (windowUsd and mode)FleetView, Budget caps, the cap box and Save capa ceiling with mode warn first, block laterScopes are repo, team and fleet; modes are off, warn and block. An empty box is no ceiling, and a scope with no priced spend can never trip.
kill-switch scope and blast patternsFleetView, Fleet kill-switch and policy gatefleet for a full halt, or team or repo with the id it applies toA halt records who and why. With no blast pattern set the halt covers everything in scope; patterns narrow it to changes that cross those paths.
policy rules (forbidden-path, max-diff, forbidden-op)FleetView, the three rule cardsoff until you have read what each would fire onEach rule carries its own mode of off, warn or block, and the rules engage only on agent-authored work at or above the 0.6 confidence floor.
REPOOPS_PROCESS_SCANthe data directory's .envleave unset unless a known agent is invisibleAnything other than exactly 1 keeps it off and no listing runs. Names only; arguments and window titles are never read.
CURSOR_ADMIN_KEY, COPILOT_METRICS_TOKEN, COPILOT_METRICS_ORGthe data directory's .envset only the vendor whose admin console you ownAn unconfigured vendor means zero outbound requests and an honest empty row rather than a failed pull.
REPOOPS_VENDOR_USAGE_PULL_HOURSthe data directory's .env12 (the default)The gap between scheduled vendor pulls; a pull inside the interval is skipped as a recent pull.
REPOOPS_OPERATOR_CONFIRM_SECRETthe data directory's .envset it on any machine more than one person can reachIt makes the three FleetView writes take an out-of-band value on top of same-origin. The Operator confirmation field appears on the tab only when it is set.
toolinventorypublish.enabled, mcpinventorypublish.enabledSettings, What this machine publishes to your teamoff unless a team page needs the inventoryOff by default. On, a bounded whitelisted snapshot goes to the bound team; the local path, the hostname and env var values never ride it.
REPOOPS_AUDIT_RETENTION_DAYSthe data directory's .env90 (the default)How long a fleet-cap, fleet-control or partner-verdict audit row stays in the daily partition before it is gzipped into the monthly archive.
ⓘ
To stop or undo
Every control is reversible and independent. Unset a flag in the .env and restart to return that control to advisory. Disengage the kill-switch from its card, which records who released it and why. Set a cap mode back to off, or clear the cap box for no ceiling. Turn the two publish toggles off to stop anything leaving the machine. Remove a vendor key to stop the pull. None of this deletes what has already been recorded; see Delete below.

What you should see

A single-machine fleet, nothing armed

Configuration. One device, no flags set, no publish toggle on, the default 90-day window.

Expect. Live agent VMs reads 1/1 while a session is fresh. The Activation console badge reads all dormant, the caps and control badges read advisory (dormant), and the shadow run reports what each control would have done over recent merges.

Verify. The refresh badge reads poll and the timestamp moves every 15 seconds. Budget caps show No cap set (off) per scope. Partner-attested controls says No partner verdict recorded yet. Record one below to see the attested facet.

A cap crossed while caps are dormant

Configuration. A fleet cap set with mode block, priced agent spend above it, REPOOPS_FLEET_CAPS unset.

Expect. The fleet card turns amber and its verdict reads Would block (set REPOOPS_FLEET_CAPS=1 to enforce). Nothing is refused: the check endpoint still answers allow.

Verify. POST /api/fleet/caps/check returns block false and wouldBlock true. The shadow row Budget caps would block (fleet) reads yes. Arming the flag and restarting flips the verdict to Blocked, refusing agent ingest and merge.

Nothing priced, nothing classified

Configuration. Merged pull requests that the capture layer could not price or classify above the floor.

Expect. Cost tiles read n/a rather than $0, Agent share reads n/a, and the low-confidence and unclassified counts appear as their own excluded buckets under the bars.

Verify. The trust and risk sections say no agent has enough classified-above-floor runs to score yet, and the loop-discipline badge reads not enough signal. No section substitutes a zero for a missing measurement.

Data and cost

What is captured
Nothing new. The rollup folds the presence-projected active-session snapshot and the per-repo autonomous-PR metric. The per-machine sections read the filesystem: the tool probe runs an existence check on each tool's known store and never reads inside it, and the MCP inventory parses each tool's own config file for server name, transport, command, args summary, URL and env var names. What FleetView writes: fleetCaps, fleetControl and agentRegistry in account-settings.json under the brain root; audit rows in the fleet-cap, fleet-control and partner-verdict partitions; a daily MCP snapshot and a daily vendor-usage file in the data directory.
Who can see it
Local by default. Two opt-in publishers can send a per-machine snapshot to a bound team over the device token. The tool inventory sends at most 64 whitelist-copied rows (tool id, display name, installed, last session seen, sessions in the window, and a vendor developer count and date). The MCP inventory sends at most 128 rows (server name, source tool, scope, transport, a command summary capped at 240 characters, up to 32 env var names, the config hash, first and last seen, and a drift count). The detected local path, the hostname, env var values and vendor developer identities never leave. Live presence broadcasts a coarse activity word and a repo key, never a path or file content.
How long it is kept
Audit rows are kept for REPOOPS_AUDIT_RETENTION_DAYS days, 90 by default, then gzipped into a monthly archive under the audit directory. Partner verdicts are read over the same 90 days for that reason. The MCP inventory keeps the newest 30 daily snapshots and vendor usage the newest 90 daily files, each pruning the rest on write. The tab reads a 90-day window; the routes accept windowDays from 1 to 365.
What leaves the machine
The rollup and both inventories make no outbound call. Vendor-reported usage calls api.cursor.com and api.github.com with your own admin keys, and makes zero requests when neither vendor is configured. Live presence is the app's own embedded WebSocket on this origin. The two hosted snapshots are the only RepoOps egress, and only while their toggle is on.
What it costs
No model call. Every section reads stored data, config files or an install path, so nothing on this tab spends tokens. The money it shows is the priced agent-PR cost the capture layer already recorded, divided by the pull requests that had a price; a scope with no priced spend reads n/a and cannot trip a cap.

When the result differs

SymptomLikely causeNext action
No active agent VMs. Sessions appear here as soon as a tracked repo runs a Claude Code session.No captured session is fresh inside the presence window on any device this brain can see.Run a session in a tracked repo. A teammate's machine appears only once that device syncs its presence and capture.
The refresh badge reads poll, not live.The server reported no team presence room: the device is not bound, or org sharing is off.Bind the device and turn org sharing on. Polling every 15 seconds is the documented fallback, not a fault.
Process scan off; set REPOOPS_PROCESS_SCAN=1 in the RepoOps data-dir .env and restart to add running-process evidence.The variable is unset, or set to something other than exactly 1.Set it to 1 in the data directory's .env and restart, or leave it off and read the adapter table alone.
No vendor admin keys configured.Neither CURSOR_ADMIN_KEY nor the COPILOT_METRICS_TOKEN and COPILOT_METRICS_ORG pair is set.Add the keys named in the empty state to the data directory's .env, restart, then press Pull now.
A cap card reads Would block (set REPOOPS_FLEET_CAPS=1 to enforce).The cap is crossed, but caps are dormant, so the decision is advisory.Leave it if you wanted advisory. To enforce, set the flag in the .env and restart.
Save cap returns Not saved, with a status rather than a message.The operator-write gate refused: a missing confirmation value, or a non-loopback bind with no secret configured (a 501 naming the variable).Paste the value of REPOOPS_OPERATOR_CONFIRM_SECRET into the Operator confirmation field, or set that variable on a machine bound to a public address.
A policy save is refused with a parent-floor message.The child policy would weaken the parent: a looser mode, a dropped forbidden path, a raised diff ceiling or a removed required control.Read the named weakenings and tighten the child, or change the parent policy first.
Lessons that worked reads not yet measurable.No applied lesson has enough run history on both sides of its first application.Nothing to fix. The tile fills in as applying runs accumulate; it never shows a rate it could not measure.
Disable
Per control, and each one on its own. Unset REPOOPS_FLEET_CAPS, REPOOPS_FLEET_KILL or REPOOPS_FOUR_EYES_GATE in the data directory's .env and restart to return that control to advisory. Disengage the kill-switch from its card, which records who and why. Set a cap mode to off or clear its box. Turn the Tool inventory and MCP inventory publish toggles off. A guardian hold is always liftable from its card with a name and a reason, whether or not the hold is armed.
Roll back
Not provided. No route restores an earlier caps or control configuration. The current values are in account-settings.json under the brain root (fleetCaps, fleetControl, agentRegistry), and the previous values survive only as audit rows in the fleet-cap and fleet-control partitions, which you can read but not replay. Signing a policy bundle records a digest and a parent digest; it does not restore one.
Revoke access
Disconnect the device in the desktop app to forget the binding and revoke the device token on the server, which stops both inventory snapshots and closes the presence room. Vendor access is your own: remove CURSOR_ADMIN_KEY or COPILOT_METRICS_TOKEN from the data directory's .env to stop the pull, then revoke the key at Cursor or GitHub, because removing it here does not revoke it there.
Delete
Not provided. No route deletes a fleet audit row, an MCP snapshot or a vendor-usage file; each ages out on its own retention (90 days for audit rows and vendor usage, the newest 30 daily files for MCP). On the hosted side the MCP receiver replaces this machine's rows on every push and deletes the ones absent from it, so unbinding the device stops the refresh but does not clear what was already sent.

Maintenance evidence

Feature id
fleetview (spine leaf fleetview)
Owner
Cloud-Native Accountability program (CNA.5 FleetView, with CNA.6, CNA.13, CNA.14, CNA.16, CNA.24, CNA.25, CNA.30, CNA.32, CNA.37, CNA.40 and CNA.44 rendering on the same tab). Guide: LDG-0717.
Supported product version
RepoOps v0.3.1
Last verified
2026-09-15, read against origin/main at 52366bb6d; labels read from the served tab source (public/fleetview.html), routes and gates from lib/routes/fleet.mjs, flag defaults and vendor key names from .env.example, and the adapter list from lib/providers/index.mjs. The registry holds 18 adapters today; the older code comments still say 17, and the code is what this guide follows.
Example fixtures
No fixture file; the shapes are inline in lib/fleet-view.test.mjs (the fold and its honest nulls), lib/routes/fleet.test.mjs, test/fleetview-caps-and-hold-override.test.mjs (the cap editor and the hold override), test/fleetview-control-and-partner-verdict.test.mjs, lib/fleet/kill-switch.test.mjs, lib/fleet/policy-bundle.test.mjs, lib/fleet/partner-controls.test.mjs, lib/fleet/activation-console.test.mjs, lib/tool-inventory.test.mjs and lib/routes/vendor-usage.test.mjs.
Source references
lib/fleet-view.mjs, lib/routes/fleet.mjs, lib/fleet/activation-console.mjs, lib/fleet/kill-switch.mjs, lib/fleet/policy-bundle.mjs, lib/fleet/partner-controls.mjs, lib/fleet/partner-verdict-store.mjs, lib/fleet/preflight.mjs, lib/budget/fleet-cap.mjs, lib/tool-inventory.mjs, lib/process-scan.mjs, lib/mcp-config-inventory.mjs, lib/vendor-usage/index.mjs, lib/ai-share.mjs, lib/audit-partition.mjs, lib/local-origin.mjs, public/fleetview.html
Documentation review
Independent review requested on the slice pull request; not yet recorded.
Video review
Story script written 2026-09-15; render and review pending in the same slice.

Last updated