Fava-client hardening cluster (CODE-REVIEW-2026-06 #7, #14, #15, #19
+ libra-#23, libra-#53):
- New FavaClient.transform_source_line does the whole
read-checksum-modify-write under the global write lock and maps
Fava 409/412 to ChecksumConflictError. The approve and reject
endpoints used to do this dance with raw httpx and no lock — two
concurrent mutations raced each other and every other ledger
writer (libra-#23). They now route through the new method and
translate conflicts to HTTP 409.
- update_entry_source / delete_entry raise ChecksumConflictError on
409/412 instead of leaking raw HTTPStatusError.
- One shared httpx.AsyncClient per FavaClient (12 per-call
instantiations removed — no more TCP handshake per request);
closed via libra_stop. Health probes keep their 2s timeout
per-request.
- Account names/patterns are validated against ^[A-Za-z0-9:_-]+$
before interpolation into BQL string literals.
- The posting amount regexes are consolidated into module-level
compiled patterns, all decimal-tolerant — the old integer-only
SATS pattern silently dropped decimal-SATS postings (Fava's @@->@
normalisation emits them) from balances.
- add-account no longer verifies its own write with a second
serialized get_all_accounts round-trip (libra-#53):
sync_single_account_from_beancount grows an assume_exists path.
New test: concurrent approve+reject must both land (was
lost-update/412 before the lock).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
113 passing tests + 3 skipped + 8 xfailed across 10 files, covering user
expense and income flow, admin receivable/revenue, settings + auth gates,
void/reject, manual payment requests, balance display, Lightning auth
paths, reconciliation API, and pure-function units. Runs against a real
Fava subprocess and full LNbits app via asgi_lifespan; the harness
captures the auth-flow / settings / env-var disciplines surfaced during
build-out (see tests/README.md and tests/conftest.py docstring).
Eight xfailed/skipped tests carry full implementations gated behind issues
#38, #39, #40 — they flip back on automatically when those land.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>