After airplanes-live/website#62 migrated config_state['alt']['value'] to float metres AMSL end-to-end, the remote-config-sync POST from the feeder is rejected by the server:
apl-feed-config-sync level=error reason=validation_failed
body={"error":"validation_failed","errors":{"fields":{"alt":"alt.value must be a number or null"}}}
feed/scripts/apl-feed/config.sh builds the outbound payload with jq --arg alt_v "$alt", which always emits a JSON string ("120m", "42.5", etc.). The website serializer in accounts/serializers/feeder.py requires int | float | None for alt.value. Every sync tick fails until REMOTE_CONFIG_ENABLED is turned off again.
The website docstring claimed feed + Go webconfig were "migrated in lockstep" — the lockstep migration on the feed side never landed.
Scope
The fix is a contract alignment: store altitude as bare metres on disk too, not just on the wire. Today's three-layer setup (unit-suffixed disk, suffix-tolerant operator input, metres-on-wire) creates an asymmetric round-trip — an operator who types 400ft ends up with 121.92m on disk after the first successful sync, with no notification.
Storage flips to bare metres end-to-end via a single shared altitude_to_bare_metres helper in scripts/lib/configure-validators.sh, used by apply, configure.sh, the new on-update migrator, and the config-sync outbound payload. Operator input stays suffix-tolerant (120m / 400ft / 120 all accepted). Range-gate moves to post-conversion metres [-1000, 10000] to match the website's contract exactly.
airplanes-live/image-webconfig follows in a second PR with the matching Go canonicalizer, SPA dirty-state comparator, and a dynamic m unit indicator next to the altitude input.
After airplanes-live/website#62 migrated
config_state['alt']['value']to float metres AMSL end-to-end, the remote-config-sync POST from the feeder is rejected by the server:feed/scripts/apl-feed/config.shbuilds the outbound payload withjq --arg alt_v "$alt", which always emits a JSON string ("120m","42.5", etc.). The website serializer inaccounts/serializers/feeder.pyrequiresint | float | Noneforalt.value. Every sync tick fails untilREMOTE_CONFIG_ENABLEDis turned off again.The website docstring claimed feed + Go webconfig were "migrated in lockstep" — the lockstep migration on the feed side never landed.
Scope
The fix is a contract alignment: store altitude as bare metres on disk too, not just on the wire. Today's three-layer setup (unit-suffixed disk, suffix-tolerant operator input, metres-on-wire) creates an asymmetric round-trip — an operator who types
400ftends up with121.92mon disk after the first successful sync, with no notification.Storage flips to bare metres end-to-end via a single shared
altitude_to_bare_metreshelper inscripts/lib/configure-validators.sh, used by apply, configure.sh, the new on-update migrator, and the config-sync outbound payload. Operator input stays suffix-tolerant (120m/400ft/120all accepted). Range-gate moves to post-conversion metres[-1000, 10000]to match the website's contract exactly.airplanes-live/image-webconfigfollows in a second PR with the matching Go canonicalizer, SPA dirty-state comparator, and a dynamicmunit indicator next to the altitude input.