The in-process Treasury plugin (LedgerServiceImpl.transferInternal) and the out-of-process treasury-rest-api (TransferService.executeTransfer) each implement the overdraft check independently (they run in different JVMs and can't share a call path). They had drifted: REST skipped the check entirely whenever allow_overdraft = true, ignoring a finite credit_limit, and used -credit_limit as the floor even when allow_overdraft = false. The plugin uses the documented three-way model.
Live impact: of 122,313 accounts, only account_id=4 "ChestShop System" (allow_overdraft=true, credit_limit=0.00) sat in a disagreeing quadrant — the plugin floors it at 0, REST treated it as unlimited. (All 33 sibling faucet/sinks use the -1 sentinel.) Admin-shops aren't used on DC, but the plugin supports them for downstream consumers, so the interpretation must be consistent.
Fix: extracted the canonical decision into io.paradaux.common.OverdraftPolicy (isUnlimited, isWithinFloor) — :common is already depended on by both treasury and treasury-rest-api, so both engines now defer to one pure function and physically cannot drift again. Canonical semantics:
allow_overdraft = false → floor 0 (credit_limit ignored)allow_overdraft = true, credit_limit < 0 → unlimited faucet/sink (the -1 sentinel)allow_overdraft = true, credit_limit ≥ 0 → floor -credit_limittrue, null path)Plugin keeps its skip-balance-lock optimization for unlimited sources (via isUnlimited) and the ADT-10 shared-flag lock. 18 unit tests in :common cover every quadrant incl. the account-4 regression. Surfaced by the 2026-07-04 codebase audit.
Follow-ups (separate): (a) confirm intended ChestShop-System semantics and set account_id=4 credit_limit=-1 if it should be an unlimited faucet; (b) optional DB-trigger floor backstop in trg_postings_ai.