A negative trip balance is over-coverage, not a bill
ci / lint-test (push) Successful in 49s

You were right and my label was wrong. A payment is allocated to a trip as a
lump sum, and the grouped-payment allocation gave each trip enough to clear the
payer's gross share. So netting the other side off leaves a fully-paid trip
negative by exactly what the payment over-covered: Europe reads -$802.75 because
Sonu paid $8,004.04 against a net share of $7,201.30. That surplus is already
carried in the overall balance, where she still owes $5,313.38 — nothing is owed
to her, and "you owe them" said the opposite.

The arithmetic never changed; only what the page claims it means. paid_to_me is
the discriminator: negative with a payment into the scope is over-coverage,
negative with no payment is genuinely owed because the viewer's share of the
other person's spending exceeds theirs. Both cases now tested. All three of
today's negatives are the first kind.

The trip table gains an Overall balance column from the unscoped participant
balances, because a single trip's figure cannot tell you whether to pay anyone,
and the footnote now says settlement is always against the overall figure.

278 passing, build clean.
This commit is contained in:
2026-08-02 21:52:51 +10:00
parent 6db2345c49
commit 2d341e24a0
3 changed files with 143 additions and 39 deletions
+28 -6
View File
@@ -292,9 +292,30 @@ for, so a viewer-as-payer figure can never contain it, and Sonu's Europe 2026
read "you are owed $2,408.24" while omitting the $8,004.04 she owed.
**The API returns both halves whole; the trip page nets them for display.** One
figure per person answers the only question the column exists for — what do we
transfer to close this trip. The breakdown renders beside it, because a net
nobody can decompose is how a wrong figure survives.
figure per person, with the breakdown beside it, because a net nobody can
decompose is how a wrong figure survives.
**A NEGATIVE trip net is not a bill — this is the trap, and it was got wrong
twice.** A payment is allocated to a scope as a lump sum, and the grouped-payment
allocation gave each trip enough to clear the payer's **gross** share. So netting
the other side off leaves a fully-paid trip negative by exactly what the payment
over-covered: Europe reads **$802.75** because Sonu paid $8,004.04 against a net
share of $7,201.30. That surplus is already carried in the overall balance —
**she still owes $5,313.38 overall** — so labelling it "you owe them" was flatly
wrong. Scope nets sum to the overall figure; a negative simply means this scope
was over-covered and the excess sits in another.
The discriminator is `paid_to_me`:
- negative **with** a payment into the scope → over-covered, nothing to pay
- negative **with no** payment → genuinely owed, because the viewer's share of
the other person's spending exceeds theirs
All three of today's negatives are the first kind (Europe Sonu $802.75,
Sonu + Sunny $936.34, Europe Molina $816.16). Both cases are tested. The trip
table therefore carries an **Overall balance** column from the unscoped
`getParticipantBalances` — the trip figure alone cannot tell you whether to pay
anyone, and **settlement is always against the overall figure, never one trip.**
**On whether to net — the reasoning reversed once, and the second answer is the
right one.** The first objection was that the grouped-payment allocation (memory
@@ -310,9 +331,10 @@ because the allocation derived her payment split *from* her gross, so it lands o
zero by construction.
Current nets: Auckland Sonu **+$1,077.25** (1,505.64 428.39), Europe Sonu
**$802.75**, Sonu + Sunny **$936.34**, Europe Molina **$816.16**. The `owed`
column itself was verified byte-identical when the mirror was added — $1,505.64,
$816.16, $0.00, $0.00.
**$802.75**, Sonu + Sunny **$936.34**, Europe Molina **$816.16** — the three
negatives all being over-coverage, per the rule above. The `owed` column itself
was verified byte-identical when the mirror was added — $1,505.64, $816.16,
$0.00, $0.00.
**A payment left on the household tab makes a settled trip debt read as
outstanding.** Payment 5 (Molina → Sonu, $1,605.49) discharged the Europe debt