wallet: the deterministic parse must beat the model's location
ci / lint-test (push) Successful in 46s
ci / lint-test (push) Successful in 46s
Found by reading the backfilled rows rather than by a test. The precedence was subtitle -> MODEL -> descriptor, so the model's answer won wherever it had one, which is always. That defeats the entire point of parsing the descriptor positionally: the ledger got "SAN FRANCISOUS" for the only foreign merchant in the capture window, plus the padded "MELBOURNE AU", and the bare "Sydney"/"Perth" with their country codes dropped. Correct order is subtitle -> deterministic descriptor -> model. After the fix those rows read SAN FRANCISO US, MELBOURNE AU, Sydney AU, Perth AU, and a subtitle-derived place still wins where one exists. The test that should have caught this passed because its fixture omitted `location`, so the fallback chain was never exercised — every real interpretation carries one. Fixture now includes the model's actual mangled answer for that row, so the test fails if the order regresses. Re-ran the backfill on the corrected code: 21 transactions, $6,284.37 unchanged, 13 with a location. The 21 previously-written rows were deleted first rather than superseded, which is normally forbidden on transactions — checked before doing it that they carried zero splits, tags and overrides, being machine-written minutes earlier by this same lane with nothing yet attached.
This commit is contained in:
@@ -223,6 +223,11 @@ describe("location", () => {
|
|||||||
await ingest(req({
|
await ingest(req({
|
||||||
...breadworldBank, event_id: 75, amount: 42.44,
|
...breadworldBank, event_id: 75, amount: 42.44,
|
||||||
merchant: "SP PEAKDESIGN",
|
merchant: "SP PEAKDESIGN",
|
||||||
|
// The model's own answer for this row, included because every real
|
||||||
|
// interpretation carries one -- omitting it is what let a wrong
|
||||||
|
// precedence order pass this test the first time, and ship
|
||||||
|
// "SAN FRANCISOUS" into the ledger.
|
||||||
|
location: "SAN FRANCISOUS",
|
||||||
notif_body: "$42.44 paid from Mastercard ending...8032. SP PEAKDESIGN SAN FRANCISOUS",
|
notif_body: "$42.44 paid from Mastercard ending...8032. SP PEAKDESIGN SAN FRANCISOUS",
|
||||||
}));
|
}));
|
||||||
const [t] = await txns();
|
const [t] = await txns();
|
||||||
|
|||||||
@@ -139,9 +139,15 @@ export async function processWalletIngestion(ev: WalletEvent): Promise<WalletIng
|
|||||||
// second must be able to REPLACE a descriptor-derived place, or the result
|
// second must be able to REPLACE a descriptor-derived place, or the result
|
||||||
// depends on arrival order.
|
// depends on arrival order.
|
||||||
const subtitleLocation = sub.location ?? null;
|
const subtitleLocation = sub.location ?? null;
|
||||||
const location = subtitleLocation
|
// Precedence: the Wallet subtitle, then the DETERMINISTIC descriptor parse,
|
||||||
?? ev.location
|
// and only then the model's own answer. Getting this order wrong defeats the
|
||||||
?? (desc.city && desc.country ? `${desc.city} ${desc.country}` : desc.city);
|
// point of parsing positionally at all -- the model returned the mangled
|
||||||
|
// "SAN FRANCISOUS" and the padded "MELBOURNE AU" for exactly these rows,
|
||||||
|
// which is what wallet-descriptor.ts exists to correct.
|
||||||
|
const descriptorLocation = desc.city && desc.country
|
||||||
|
? `${desc.city} ${desc.country}`
|
||||||
|
: desc.city;
|
||||||
|
const location = subtitleLocation ?? descriptorLocation ?? ev.location ?? null;
|
||||||
|
|
||||||
// Currency: only ever what the notification actually states. A bare "$" is
|
// Currency: only ever what the notification actually states. A bare "$" is
|
||||||
// recorded as unknown, never assumed AUD -- harmless domestically, a 23x
|
// recorded as unknown, never assumed AUD -- harmless domestically, a 23x
|
||||||
|
|||||||
Reference in New Issue
Block a user