Summary
The swap state file is shared across checkouts ($XDG_STATE_HOME/libtmux-mcp-dev/swap/state.json, not per-worktree). A key naming a CLI the running build does not know makes load_state raise, and revert and use-local then refuse to do anything — including for the entries that build understands perfectly well. There is no --force and no forget subcommand, so the only recovery is editing the state file by hand.
_parse_state_key documents the opposite intent: "Hand-edited or unrecognised keys return None so load_state can drop them without crashing."
This is reachable in normal use: run a branch that adds a CLI, then run trunk against the same $HOME.
Reproduction
run() { HOME="$SB" XDG_CONFIG_HOME="$SB/.config" XDG_STATE_HOME="$SB/.local/state" uv run "$1" "${@:2}"; }
Swap with a build that knows opencode, then use one that does not:
run ./scripts/mcp_swap.py use-local --repo "$PWD" --cli cursor --cli opencode --no-preflight
run /tmp/released.py revert
Expected
An unrecognised key belongs to a build that cannot manage it. Report it and skip it, so entries this build does understand can still be reverted — which is what _parse_state_key's docstring already promises.
Actual
swap state has invalid key 'opencode:user': <sandbox>/.local/state/libtmux-mcp-dev/swap/state.json
Same for use-local. doctor uses the non-strict load and silently drops the key, so the one read-only diagnostic hides the condition that is blocking everything else.
References
Summary
The swap state file is shared across checkouts (
$XDG_STATE_HOME/libtmux-mcp-dev/swap/state.json, not per-worktree). A key naming a CLI the running build does not know makesload_stateraise, andrevertanduse-localthen refuse to do anything — including for the entries that build understands perfectly well. There is no--forceand noforgetsubcommand, so the only recovery is editing the state file by hand._parse_state_keydocuments the opposite intent: "Hand-edited or unrecognised keys returnNonesoload_statecan drop them without crashing."This is reachable in normal use: run a branch that adds a CLI, then run trunk against the same
$HOME.Reproduction
Swap with a build that knows
opencode, then use one that does not:Expected
An unrecognised key belongs to a build that cannot manage it. Report it and skip it, so entries this build does understand can still be reverted — which is what
_parse_state_key's docstring already promises.Actual
swap state has invalid key 'opencode:user': <sandbox>/.local/state/libtmux-mcp-dev/swap/state.jsonSame for
use-local.doctoruses the non-strict load and silently drops the key, so the one read-only diagnostic hides the condition that is blocking everything else.References
load_statestrict path atv0.1.0a20_parse_state_key, whose docstring describes dropping unknown keys