Skip to content

State File Schema

Fleet’s state file is the single source of truth for per-harness skill enablement. Harness config files are outputs derived from it; sync projects it, doctor compares against it, and nothing else writes it.

  • Location: ~/.config/fleet/state.json (FLEET_HOME overrides the home root)
  • Companion: ~/.config/fleet/config.json sits beside it and holds the machine-local customs settings ({"skillsDirs": ["/abs/collection"], "adoptTarget": "/abs/collection", "hiddenHarnesses": ["bob"]}, FLEET_HOME-aware): the ordered list of tracked collection dirs in precedence order (managed by fleet skill add-dir and fleet skill remove-dir), the adopt-target collection dir (absent means the fleet-home fallback), and the harnesses hidden on this machine (managed by fleet harness hide and unhide). The retired skillsRepos key is preserved as an unknown field and never read. It is not part of the state file — see fleet config get/set/unset/list in the command reference. Enablement stays in state.json; the tracked set and adopt default stay in config.json.
  • Version: 1
{
"version": 1,
"skills": {
"tdd": {
"harnesses": {
"claude": "off",
"codex": "off",
"opencode": "off",
"pi": "off"
}
}
}
}
Field Type Meaning
version number, required Schema version. Fleet reads and writes 1.
skills object, optional One entry per skill the state mentions. Absent = nothing disabled anywhere.
skills.<name>.harnesses object, optional Per-harness values. Fleet writes only "off".
skills.<name>.<other> any Unknown per-skill fields (see forward compatibility).
<other> (top level) any Unknown top-level fields.

Design rules:

  • Sparse, disable-only. An entry exists only to record a disable. A skill with no entry is on everywhere, and once a skill’s last disable is removed the whole entry disappears. There are no “on” records.
  • A disable outlives its skill. If you uninstall a skill while it is disabled, its entry stays as dormant intent: a reinstall comes back disabled, but sync writes no new config entry for a skill installed nowhere. The entry remains until fleet skill prune removes it, which loses the disable-on-reinstall behavior.
  • Keyed by skill name (the frontmatter name, falling back to the directory name), not by directory, because harness rules target the name. Harness keys are fleet’s harness IDs: opencode, pi, codex, claude, cursor, bob.

The schema is built so newer or foreign writers lose nothing:

  • Unknown fields are preserved verbatim on every round-trip: unknown top-level fields, unknown fields inside a skill entry, and harness values fleet doesn’t recognize. They are rendered after fleet’s own fields, in sorted order.
  • Enabling never destroys what it doesn’t understand. Removing a disable deletes only the exact "off" value it wrote; a future value (say, an object with scheduling metadata) stays. A skill entry with unknown fields survives even when its harnesses map empties.
  • Version is a hard stop, not a guess. A file with a version other than fleet’s is an error (~/.config/fleet/state.json: state file version 2 is newer than fleet's (want 1)), as is a file with no version at all. Fleet never silently reinterprets content it doesn’t recognize; upgrade the binary instead.
  • A missing file is an empty state, not an error. Every fleet command works on a fresh machine.
  • Atomic. The file is written to a temp file and renamed, so a crash cannot truncate it.
  • Canonical formatting. Two-space indent, fleet’s fields first in fixed order (version, then skills), unknown fields after in sorted order. Hand-editing is fine — the next command re-reads the file as truth — but the formatting normalizes on the next write.
  • Fixed key order, sorted skill names. Output is deterministic, which keeps diffs between runs meaningful.

Fleet never writes the skills CLI’s lockfile (~/.agents/.skill-lock.json), and the skills CLI knows nothing about this file — the two formats coexist. The schema keeps the shape of the upstream skills CLI’s enable/disable proposal (PR #641’s enabled field) in mind, so a later convergence with upstream stays possible.