Noticed while deploying v0.10.0.
The gap
keel/commands/update.py downloads and installs exactly four wheels:
PRODUCTION_WHEEL_PREFIXES: tuple[str, ...] = (
"keel_core",
"keel_broker_api",
"keel_broker_coinbase",
"keel_trader",
)
That list predates keel-broker-alpaca (#382), and it is stated by name rather than derived — deliberately, and rightly: Release/*.whl would drag in keel_broker_fake (registers a fake venue entry point) and keel_broker_robinhood (pulls an Ed25519 stack for an adapter nothing constructs). Naming the set is the correct shape. The set is just now incomplete.
Since #386 there is a tracked config, config.paper-equities.yaml, whose broker: name: alpaca selects an adapter that self-update will never install.
What actually happens on such a deployment
keel update succeeds. keel_core, keel_broker_api, keel_broker_coinbase and keel_trader move to the new version — and keel_broker_alpaca stays where it was.
Then keel versions runs, and it checks every keel distribution in the venv:
A partial upgrade fails it, with the numbers: error: PARTIAL INSTALL: 4 keel distributions at 2 different versions
So the verify step should catch it. Which means the likely failure mode is not a silent mixed install but a self-update that always fails at verification on an equities deployment, after the wheels are already in — the "failure AFTER a finished install" path, where the updater re-installs the previous wheels best-effort and points at the manual procedure. Worth confirming that is what happens rather than assuming; it is also possible the mixed set is reported as an error the operator reads as spurious and overrides.
Either way an equities deployment cannot use the shipped update path today, and v0.10.0 is the first release where that is reachable, since it is the first to publish keel_broker_alpaca alongside a config that selects it.
The awkward part
"Production wheels" is no longer one set. A Coinbase deployment must not have Alpaca's requests dependency forced on it, and an Alpaca deployment must not be missing it — but the wheel list is a module constant, while which venue a deployment runs is a property of its config.
Options, none obviously right:
- Derive the venue wheel from the running config's
broker: name:. Most correct, most coupling — the updater would have to read config to decide what to download.
- Install every venue wheel that is ALREADY present in the venv, keeping the four as the floor. Self-describing, needs no config, and matches what an upgrade means: move what is installed. A deployment that has
keel_broker_alpaca gets it upgraded; one that does not, does not.
- Add
keel_broker_alpaca to the constant unconditionally. Simplest, and puts an unused adapter plus requests on every Coinbase box — the thing the named-set rule exists to prevent.
(2) looks strongest: it needs no new input, cannot install a venue a deployment did not already choose, and keeps keel versions' all-must-agree invariant true by construction.
Also stale
docs/RELEASING.md's "Release assets" table has no row for keel_broker_alpaca — it describes six distributions and the workspace now has seven. Nothing functional depends on it (uv build --all-packages builds it, the workflow publishes it, and keel versions reads installed metadata rather than a fixed list), but the table is what a reader consults to know which wheels a deployment takes.
Noticed while deploying
v0.10.0.The gap
keel/commands/update.pydownloads and installs exactly four wheels:That list predates
keel-broker-alpaca(#382), and it is stated by name rather than derived — deliberately, and rightly:Release/*.whlwould drag inkeel_broker_fake(registers afakevenue entry point) andkeel_broker_robinhood(pulls an Ed25519 stack for an adapter nothing constructs). Naming the set is the correct shape. The set is just now incomplete.Since #386 there is a tracked config,
config.paper-equities.yaml, whosebroker: name: alpacaselects an adapter that self-update will never install.What actually happens on such a deployment
keel updatesucceeds.keel_core,keel_broker_api,keel_broker_coinbaseandkeel_tradermove to the new version — andkeel_broker_alpacastays where it was.Then
keel versionsruns, and it checks every keel distribution in the venv:So the verify step should catch it. Which means the likely failure mode is not a silent mixed install but a self-update that always fails at verification on an equities deployment, after the wheels are already in — the "failure AFTER a finished install" path, where the updater re-installs the previous wheels best-effort and points at the manual procedure. Worth confirming that is what happens rather than assuming; it is also possible the mixed set is reported as an error the operator reads as spurious and overrides.
Either way an equities deployment cannot use the shipped update path today, and
v0.10.0is the first release where that is reachable, since it is the first to publishkeel_broker_alpacaalongside a config that selects it.The awkward part
"Production wheels" is no longer one set. A Coinbase deployment must not have Alpaca's
requestsdependency forced on it, and an Alpaca deployment must not be missing it — but the wheel list is a module constant, while which venue a deployment runs is a property of its config.Options, none obviously right:
broker: name:. Most correct, most coupling — the updater would have to read config to decide what to download.keel_broker_alpacagets it upgraded; one that does not, does not.keel_broker_alpacato the constant unconditionally. Simplest, and puts an unused adapter plusrequestson every Coinbase box — the thing the named-set rule exists to prevent.(2) looks strongest: it needs no new input, cannot install a venue a deployment did not already choose, and keeps
keel versions' all-must-agree invariant true by construction.Also stale
docs/RELEASING.md's "Release assets" table has no row forkeel_broker_alpaca— it describes six distributions and the workspace now has seven. Nothing functional depends on it (uv build --all-packagesbuilds it, the workflow publishes it, andkeel versionsreads installed metadata rather than a fixed list), but the table is what a reader consults to know which wheels a deployment takes.