Просмотр исходного кода

Reframe negative postings as offset positions, not liabilities

A negative posting is not always a liability in the accounting sense. In
this UTXO model it is the offset side of value that enters/leaves the
ledger or is issued, sinked, or balanced internally — calling a bank's
negative posting a 'liability' misleads, since the external account acts
as the outside world rather than a creditor.

Reframe the definition across docs and Rust doc/field comments: positive
= value controlled by the account, negative = offset position allowed
only on accounts whose policy permits issuance, external flow, or system
balancing. Keep 'liability' only as one example under SystemAccount,
where it is accounting-accurate. No behavior or validation change.
Cesar Rodas 1 неделя назад
Родитель
Сommit
a26195a4fb
6 измененных файлов с 25 добавлено и 20 удалено
  1. 11 6
      crates/kuatia-types/src/lib.rs
  2. 3 3
      doc/accounts.md
  3. 2 2
      doc/architecture.md
  4. 1 1
      doc/crates.md
  5. 4 4
      doc/glossary.md
  6. 4 4
      doc/transfers.md

+ 11 - 6
crates/kuatia-types/src/lib.rs

@@ -536,6 +536,10 @@ pub enum PostingStatus {
 }
 
 /// A signed amount of one asset, owned by exactly one account.
+///
+/// A positive posting is value controlled by the account; a negative posting is
+/// an offset position (issuance, external flow, or system balancing), only
+/// allowed on `SystemAccount` or `ExternalAccount`.
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct Posting {
     /// Unique identifier derived from the creating transfer.
@@ -544,7 +548,7 @@ pub struct Posting {
     pub owner: AccountId,
     /// The asset this posting denominates.
     pub asset: AssetId,
-    /// Signed: positive = holding, negative = liability.
+    /// Signed: positive = value controlled by the account, negative = offset position.
     pub value: Cent,
     /// Lifecycle state — only `Active` postings count toward balance.
     pub status: PostingStatus,
@@ -565,7 +569,7 @@ pub struct NewPosting {
     pub owner: AccountId,
     /// The asset this posting denominates.
     pub asset: AssetId,
-    /// Signed amount: positive = holding, negative = liability.
+    /// Signed amount: positive = value controlled by the account, negative = offset position.
     pub value: Cent,
     /// Informational provenance — who funded this posting.
     pub payer: Option<AccountId>,
@@ -729,7 +733,8 @@ pub enum AccountPolicy {
     UncappedOverdraft,
     /// Fees, settlement, market-making, minting. No balance constraints.
     SystemAccount,
-    /// Boundary account holding the negative side of deposits.
+    /// Boundary account representing value entering/leaving the ledger; holds
+    /// the offset (negative) side of deposits.
     ExternalAccount,
 }
 
@@ -825,7 +830,7 @@ pub struct Movement {
     pub to: AccountId,
     /// Asset to transfer.
     pub asset: AssetId,
-    /// Amount to transfer (may be negative for liability postings).
+    /// Amount to transfer (may be negative for offset postings).
     pub amount: Cent,
 }
 
@@ -880,8 +885,8 @@ impl TransferBuilder {
         self.movement(from, to, asset, amount)
     }
 
-    /// Add a deposit: creates a liability on the external account and credits
-    /// the target account.  Pushes two movements whose net debit on the
+    /// Add a deposit: creates an offset posting on the external account and
+    /// credits the target account.  Pushes two movements whose net debit on the
     /// external account is zero.
     pub fn deposit(
         self,

+ 3 - 3
doc/accounts.md

@@ -28,7 +28,7 @@ Each account has a policy that controls what balance constraints apply:
 | `SystemAccount` | None | Yes | No |
 | `ExternalAccount` | None | Yes | No |
 
-Only `SystemAccount` and `ExternalAccount` may hold negative postings (liabilities). Validation rejects any transfer that would create a negative posting on another account type.
+Only `SystemAccount` and `ExternalAccount` may hold negative postings (offset positions). Validation rejects any transfer that would create a negative posting on another account type.
 
 `CappedOverdraft` accounts emit CAS (Compare-And-Swap) guards during validation to prevent write-skew — two concurrent transfers could each pass validation independently but together push the balance below the floor.
 
@@ -93,11 +93,11 @@ Hold positive postings only. Cannot go negative. Used for end-user wallets, merc
 
 ### System accounts (`SystemAccount`)
 
-Operational accounts for fees, settlement, market-making. Can hold negative postings (liabilities). Used as the counterparty in deposits — the system account takes on a negative balance to represent the liability.
+Operational accounts representing issuance, sink, revenue, COGS, fees, or internal balancing. Can hold negative postings (offset positions — e.g. a liability when the account is the deposit counterparty). Used as the counterparty in deposits — the system account takes on a negative balance to offset the value credited elsewhere.
 
 ### External accounts (`ExternalAccount`)
 
-Boundary accounts representing entities outside the ledger (banks, payment processors). Like system accounts, they can hold negative postings. Used to track money entering and leaving the system.
+Boundary accounts representing the outside world (banks, payment processors). They represent value entering and leaving the ledger boundary, and like system accounts they can hold negative postings (offset positions).
 
 ### Credit accounts (`CappedOverdraft`)
 

+ 2 - 2
doc/architecture.md

@@ -2,7 +2,7 @@
 
 ## UTXO (Unspent Transaction Output)-Style Postings
 
-Value is stored as **postings** — signed amounts of a single asset owned by exactly one account. A positive posting is a holding; a negative posting is a liability (used by external/boundary accounts).
+Value is stored as **postings** — signed amounts of a single asset owned by exactly one account. A positive posting is value controlled by the account; a negative posting is an offset position (issuance, external flow, or system balancing).
 
 Account balance = sum of active postings for that (account, asset) pair. There is no mutable balance field to drift out of sync.
 
@@ -194,7 +194,7 @@ Each account has a policy controlling its balance floor and whether it may hold
 | `SystemAccount` | None | Yes | No |
 | `ExternalAccount` | None | Yes | No |
 
-Only `SystemAccount` and `ExternalAccount` may receive negative postings (liabilities). Validation rejects any transfer that would create a negative posting on another account type.
+Only `SystemAccount` and `ExternalAccount` may receive negative postings (offset positions). Validation rejects any transfer that would create a negative posting on another account type.
 
 ## CAS (Compare-And-Swap) Guards for CappedOverdraft
 

+ 1 - 1
doc/crates.md

@@ -130,7 +130,7 @@ Transfers are built via `TransferBuilder` and committed with `ledger.commit(tran
 | Builder method | Description |
 |---------------|-------------|
 | `.pay(from, to, asset, amount)` | Single movement between accounts |
-| `.deposit(to, asset, amount, external)` | Two movements: liability on external + credit on target |
+| `.deposit(to, asset, amount, external)` | Two movements: offset on external + credit on target |
 | `.withdraw(from, asset, amount, external)` | Single movement from account to external |
 | `.movement(from, to, asset, amount)` | Raw movement for custom operations |
 

+ 4 - 4
doc/glossary.md

@@ -6,8 +6,8 @@
 
 A signed amount of one asset owned by one account. The fundamental unit of value in the ledger. Postings are immutable once created — consumed postings are marked `Inactive` but never deleted.
 
-- **Positive posting**: a holding (the account owns value).
-- **Negative posting**: a liability (only allowed on `SystemAccount` and `ExternalAccount`).
+- **Positive posting**: value controlled by the account.
+- **Negative posting**: an offset position — only allowed for accounts whose policy permits issuance, external flow, or system balancing (`SystemAccount`, `ExternalAccount`).
 
 Lifecycle: `Active` → `PendingInactive` (reserved by saga) → `Inactive` (consumed).
 
@@ -121,7 +121,7 @@ let deposit = TransferBuilder::new()
     .build();
 ledger.commit(deposit).await?;
 // Alice: +10,000 USD
-// Bank: -10,000 USD (liability — money entered the system)
+// Bank: -10,000 USD (offset — value entered the ledger boundary)
 ```
 
 **Alice trades 5,000 USD for EUR at 1:0.92:**
@@ -237,7 +237,7 @@ let receipt = TransferBuilder::new()
     .build();
 ledger.commit(receipt).await?;
 // Warehouse: +50.000 rice
-// World: -50.000 rice (liability — issued into system)
+// World: -50.000 rice (offset — issued into the ledger)
 ```
 
 **Cash sale — customer buys 2 rice at 15,000 Gs each:**

+ 4 - 4
doc/transfers.md

@@ -18,7 +18,7 @@ struct Movement {
     from: AccountId,  // account being debited
     to: AccountId,    // account being credited
     asset: AssetId,   // asset to transfer
-    amount: Cent,     // amount (may be negative for liabilities)
+    amount: Cent,     // amount (may be negative for offset postings)
 }
 ```
 
@@ -46,7 +46,7 @@ Resolve selects postings from A to cover 50, creates a +50 posting on B, and ret
 
 ### Deposit
 
-Fund an account from a system/external source. Creates a liability on the source and a credit on the target.
+Fund an account from a system/external source. Creates an offset posting on the source and a credit on the target.
 
 ```rust
 TransferBuilder::new()
@@ -61,9 +61,9 @@ Produces two movements:
 | external | external | USD | -100 |
 | external | to | USD | +100 |
 
-The first movement creates a -100 liability posting on the external account. The second creates a +100 posting on the target account.
+The first movement creates a -100 offset posting on the external account. The second creates a +100 posting on the target account.
 
-Net debit on the external account: -100 + 100 = **0**. No posting selection is needed — the liability is created directly.
+Net debit on the external account: -100 + 100 = **0**. No posting selection is needed — the offset is created directly.
 
 Conservation: created sum = -100 + 100 = 0. Consumed sum = 0. Both sides balance.