Phase-1 observability per coord-log §2026-06-01T07:00Z (option A
locked: always record, no enforce_fee_match gate):
fee_mismatch_sats = bitspire_fee_sats - (platform_fee_sats + operator_fee_sats)
Positive = bitspire over-reported; negative = under-reported; zero =
exact match. Recorded unconditionally on every settlement; WARN-
logged via loguru only when |delta| > tolerance, where
tolerance = max(1, int(principal_sats * 0.001)) — 1-sat floor with
0.1% relative ceiling.
bitspire.py:parse_settlement:
- Computes the delta after split_principal_based returns.
- WARN log line carries bitspire_fee_sats / expected / delta /
tolerance / principal / both fractions / tx_type / machine-npub
prefix for triage queries.
- Always stamps fee_mismatch_sats onto CreateDcaSettlementData.
- Comment explains the pre-Layer-3 expectation: large deltas are
expected while the ATM hardcodes 7.77% cash-out (aiolabs/lamassu-
next#57); the data here will quiet once Layer 3 ships.
crud.py:create_settlement_idempotent: extends the INSERT to persist
the new column.
Tests:
- tests/conftest.py: `loguru_capture` fixture — loguru routes to a
pre-bound stderr sink that pytest's caplog (stdlib only) misses and
capsys can't see; the fixture adds a list-sink for the test's
duration. Reusable for future log-behavior tests.
- tests/test_fee_mismatch_recording.py: 8 cases covering exact-match
zero delta, bitspire over- and under-reporting, the pre-Layer-3
large-delta scenario, within-tolerance silence, over-tolerance
warning, diagnostic-fields presence in the WARN line, and the
1-sat floor on tiny-principal settlements.
164/164 tests green. Phase 2 (reject on out-of-tolerance) lands as
a follow-up once observability data justifies the tighter posture.
Refs: aiolabs/satmachineadmin#38 (Layer 1), coord-log §2026-06-01T07:00Z
(lnbits advisory + option A lock).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>