kuatia-types (AccountFlags, Account), kuatia-core
(validate.rs, posting_resolution.rs), kuatia-storage-sqlADR-0004 modeled per-account
balance rules as a closed AccountPolicy enum with five variants
(NoOverdraft, CappedOverdraft { floor }, UncappedOverdraft,
SystemAccount, ExternalAccount). In practice only one distinction earned
its keep: may this account's balance go negative, or not? The capped floor, the
System/External labels, and the Uncapped variant all resolved to the same
runtime behavior (overdraft allowed, no floor), while adding an enum to
serialize, a SQL column, resolve/validate match arms, and a dashboard DTO.
We want a single, legible knob for that one question.
AccountFlags round-trip
and needs no separate enum, column, or DTO.debit_must_not_exceed_credit names the invariant
directly.AccountPolicy enum (ADR-0004)Cons:
CappedOverdraft)Cons:
AccountFlags::DEBIT_MUST_NOT_EXCEED_CREDIT bitThe account either carries the flag (balance may not go negative, no negative posting allowed) or does not (overdraft allowed without bound; a shortfall becomes a negative offset posting; the transfer records as long as it conserves value per asset). Overdraft is the default.
Pros:
AccountFlags bitfield and its storage round-trip
(a system-range bit), so there is no new column beyond dropping policy.Account::debit_must_not_exceed_credit(id) names the invariant.Cons:
System/External intent labels are gone; a boundary
account is now just an ordinary overdraft-permitting account.Chosen option: Option 3, a single DEBIT_MUST_NOT_EXCEED_CREDIT flag, with
overdraft allowed by default. Validation forbids a negative posting, and rejects
a negative projected balance, only for accounts carrying the flag. Resolution
covers a shortfall with a negative offset posting only for accounts that permit
overdraft. The policy column is dropped from the SQL schema (migration
006_drop_policy.sql).
AccountPolicy enum, no policy column, no policy DTO.SystemAccount/ExternalAccount debited past its available postings
now absorbs the shortfall as a negative offset posting instead of failing with
InsufficientFunds. This is moot for deposits (they net to zero on the
boundary account) but is a behavior change for a direct over-debit.policy from the Account canonical preimage bumps
CANONICAL_VERSION (4 → 5), changing account snapshot hashes. Persisted
transfers keep their original EnvelopeIds and stay self-consistent; a saga
in flight across the upgrade re-validates on recovery and aborts cleanly (or
rolls forward) rather than corrupting state, per ADR-0003.