# Sovereign Fleet

> Task queue, kernel sandbox, gates, router and dashboard for running coding-agent CLIs (Claude Code, OpenCode, Goose, Pi, Aider) on your own machine. Alpha, 0.6.1.

## What it is
- FastAPI daemon (`fleet daemon`, loopback by default): task queue in `~/.fleet/spool/tasks.json` (flock, atomic writes), model roster, settings, dashboard, background jobs (daily lane health check, issue intake). Every /api route needs the API token (Bearer or the dashboard's HttpOnly session cookie).
- Backends: `local` (the package's own queue; with `backend.kind = "local"` the daemon runs queued tasks itself: oldest first, dependencies settled, up to `backend.max_concurrent` at once, recovers interrupted tasks at start, prunes worktrees; `fleet run "<prompt>"` or `fleet run --task <id>` runs tasks directly) and `grid` (a bridge to an existing grid runner, which executes everything queued).
- A run: git worktree on branch `fleet/<task-id>`, sandboxed agent CLI (worktree as cwd and $PWD), agent commits folded back into the worktree, then gates, then the changes committed to that branch (also on failure), then landing. A run that leaves no change does not pass.
- Execution lifecycle: rule-based failure autopsies for the KB learning loop, atomic receipts in `<spool>/receipts/<task_id>.json`, and automated cross-lane review loops (`reviews/<project>/<task_id>.md`) with auto-fix tasks for critical/high findings.
- Sandbox (`fleet.core.sandbox`): Landlock ABI >= 6 (filesystem allow-list, TCP port allow-list, scope), no_new_privs, seccomp block of AF_UNIX sockets and io_uring. Fails closed; only FLEET_SANDBOX=0 disables it.
- Gates (`fleet.core.gates`): scope against `allowed_paths`, Python syntax, the repo gate (`projects.<name>.gate` or scripts/verify.sh, verify.sh, pytest), the task gate (`pytest:<path>` / `npm:<script>`), claimed files. Sandboxed, without credentials.
- Landing (`fleet.core.landing`): per project `branch`, `merge` (rebase, re-gate, fast-forward, push) or `pull_request` (GitHub via gh, Gitea via its API; `Closes #n` for a task from an issue).
- Router (`fleet.core.router`, `fleet.core.models`): supports both named lanes (`[lanes.<name>]`) ranked by measured pass rate per category over cost, and decoupled `(harness, model)` pairs (`[models.<name>]` + `HarnessPlugin`) ranked via Thompson sampling on cost-weighted minutes per correct result (`routing.priors_file` / `bakeoff_priors.json`). Escalation with one repair pass, quota windows after provider limits, `max_parallel`, local model residency, dead lanes from the health check left out, and strict `is_sensitive` privacy filtering. `fleet router rank|shadow`, `fleet harnesses list|test`.
- Goose Recipes & Evals (`recipes/*/recipe.yaml`, `docs/EVALS_AND_BENCHMARKS.md`): Goose 1.0.0 recipes (`fleet-ab-proposal-eval/recipe.yaml`, `fleet-self-improvement/recipe.yaml`, `fleet-bench-runner/recipe.yaml`) loaded on demand via `goose run --recipe`.
- Lane health (`fleet.core.lane_health`, `fleet.core.catalog`): `fleet lanes check [--apply]`, daily at `routing.health_hour`; a tiny request per API lane with its real key, `probe_url` for local lanes, `opencode models` for opencode lanes. Retired models (HTTP 410, or gone and deprecated on models.dev) get a probed same-family successor; `routing.discover` turns new free models into `auto_*` lanes.
- Issue intake (`fleet.plugins.intake`): `[projects.<name>.intake]`; Gitea or GitHub issues become tasks; only listed `authors` with a valid ```fleet``` block run unattended; one task per issue; results go back to the issue. `fleet intake status|sync`.
- Provider keys per lane (`key_env`, `key_file`, or a `CredentialPlugin`); a key reaches only its own CLI run. The package ships no key rotation.
- Autonomy tiers: manual_gate, safe_auto (risky words in the task text wait for approval; not a security boundary), full_swarm.
- MCP server (`python -m fleet.api.mcp_server`, stdio): fleet_dispatch_task, fleet_list_tasks, fleet_get_task_status, fleet_get_task_receipt, fleet_approve_task, fleet_reject_task, fleet_cancel_task, fleet_retry_task, fleet_system_health, fleet_lane_health, fleet_issue_intake, fleet_clear_spool. Errors carry isError.
- Dashboard: tasks (prompt first, open work by default, a strip only when something needs approval or failed), agents (one lane list, online by default), settings (autonomy, then folded groups incl. issue sources and coordinator).

## Plugin interfaces (`src/fleet/plugins/base.py`)
- `HarnessPlugin.execute_task(task, worktree_dir, timeout, model, args, pass_env, env)`; subclass `CliHarness` (name, binary, state_paths, build_command) to add a sandboxed CLI.
- `CredentialPlugin` (`get_active_key`, `record_rate_limit`, optional `record_success`, `status`), loaded with `[plugins] credentials`.
- `InputPlugin`: the issue intake is one (`IssueIntakePlugin`); trackers implement `IssueTracker` (`list_open`, `comment`, `close`). Matrix output notifications and approvals live in output_matrix.py.

## Key files
- `src/fleet/api/app.py` (API), `background.py` (daily check, intake poller, executor loop), `auth.py` (token), `cli.py`, `mcp_server.py`, `dashboard.html`
- `src/fleet/core/engine.py`, `executor.py`, `router.py`, `gates.py`, `landing.py`, `lane_health.py`, `quirks.py`, `quota.py`, `sandbox.py`, `worktree.py`, `taskstore.py`, `config.py`, `settings.py`, `backends.py`, `receipts.py`, `review.py`
- `src/fleet/plugins/intake.py`, `landing.py`, `harness_base.py`
- `docs/MIGRATION.md`: from the grid runner to this package, phase by phase.
