Wires the new directional fee fields through the write path and adds
the 15%-per-direction cap guard at the API boundary.
CRUD:
- create_machine INSERT includes operator_cash_in_fee_fraction +
operator_cash_out_fee_fraction (Pydantic default 0 covers existing
callers).
- update_machine + update_super_config already use generic update_data
dict, so the new fields flow through without per-call changes.
API boundary (views_api.py):
- _assert_machine_fee_cap_safe(operator_in, operator_out) — pairs
candidates against current super-config, rejects if (super_X +
operator_X) > 0.15 for either direction. Called from api_create_machine
+ api_update_machine (with partial-PATCH semantics: unset fields keep
the machine's current value).
- _assert_super_config_cap_safe(new_super_in, new_super_out) — fetches
every active machine; rejects with offending-machine name in the 400
detail if any (effective_super + operator) > cap. Called from
api_update_super_config.
Cap rounding: float arithmetic rounds (super + operator) to 4 decimals
(DECIMAL(10,4) precision) before comparing, so the IEEE 754 surprise
0.10 + 0.05 = 0.15000000000000002 doesn't trip the cap.
Tests (13 cases, all green): both directions hit the cap, exact-cap
acceptance, no-super-config degenerate path, partial PATCH on
super-config, offending-machine name in error detail, empty-fleet
vacuous safety.
Refs: aiolabs/satmachineadmin#38 (Layer 1), coord-log §2026-06-01T07:22Z
(cap lock at 15% per direction, defense in depth).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>