The 2026-10-17 comment supersedes the original implementation plan: SYSTEM accounts were hidden from counterparty/richest widgets, while full pairing was shelved. The remaining open issue is double-counted money-flow/volume for historical and other Vault bridge traffic. Preserve that remaining scope; do not resurrect the rejected hot-path pairing trigger.
Many plugins move money through a per-plugin SYSTEM clearing account because they use the legacy Vault economy API (withdraw + deposit, no atomic transfer) — see PAR-158 (the forward fix for ChestShop). When the economic event is really a transfer (A pays B), it lands as two ledger txns bridged by the SYSTEM account, so the explorer shows "paid ChestShop System" / "received from ChestShop System" instead of the real counterparty, double-counts volume, and surfaces the SYSTEM accounts as whales (account #4 = 1.26M postings).
This issue is the read/presentation-layer cleanup only. We explicitly decided not to rewrite the ledger — it stays append-only and auditable.
Classification of the SYSTEM clearing accounts (prod, credits_in vs debits_out):
| account | legs | net | nature |
|---|---|---|---|
| ChestShop System | 1.26M | $0 | transfer (buyer→seller) |
| PostmanPat | 4.8k | $0 | transfer ($23.2M postage) |
| Exams | 73k | $0 | transfer |
| nightcore | 90k | $0 | transfer |
| DemocracyRobbery | 17.5k | $0 | transfer (victim→robber) |
| CustomFishing / DeluxeMenus / DiscordSRV | — | $0 | transfer |
| GriefPreventionUI | 510 | +$369,880 (debits_out=$0) | pure sink — claim blocks burned |
| GriefPrevention / GriefPrevGUI | — | +$21k / +$37k | sink-heavy |
| BountyHunters | 2.1k | +$62k | mix (transfers + unclaimed sink) |
| MobsToEggs / Essentials / DemocracyPost | — | +$31k / +$14k / +$441 | mix |
A blanket "hide SYSTEM legs" would erase $369k of real claim-block spending from players' histories. So the rule is per economic event:
player→SYSTEM leg has a matching SYSTEM→player leg for the same event (same SYSTEM account, equal amount, same message, tight time window) → present as one buyer→seller transfer and hide the SYSTEM legs.SYSTEM→player: exam/fishing/mob rewards = real money entering) or sink (player→SYSTEM: claim blocks = real money leaving) → keep showing as SYSTEM↔player.Account detail (txn list, counterparties, activity calendar), money-flow (the phantom SYSTEM↔PERSONAL arrows), economy stats / volume / velocity (stop double-counting), top earners, /me, search. Exclude the SYSTEM clearing accounts from per-account / "top accounts" / counterparty rollups.
A read-layer transform (DAL view/CTE or a service) that resolves the real counterparty by pairing legs through the sentinel-owned AccountType.SYSTEM accounts. Notes:
(message, amount) group is fine for aggregates/display, but don't assert a 1:1 in any audit/forensic view — show raw there.buyer ↔ seller (no "ChestShop System") in the explorer; claim-block purchases still show player → GriefPreventionUI as a sink.Follow-up to PAR-158 (forward fix) / relates to PAR-157 (account perf) and PAR-134 (read mirror).
Draft PR: https://github.com/ParadauxIO/hibernia-economy/pull/85
Branch: par-159-explorer-volume; commit 67f20c8cae20f4f4ac4c3991202797e732ef15df; target develop (base inspected at 4339da00).
Implemented only explorer aggregate correction: daily volume/counts, period comparison, stats volume and money-flow. Same-SYSTEM preceding-posting pairs require equal opposite amounts, byte-equal plugin/normalized message and 0–1s settlement separation, plus balanced two-posting legs with non-SYSTEM counterparties. Event date is the outgoing leg. Unmatched faucets/sinks remain; UI/admin docs label this as an estimate. PAR-235 independently retains strict unique sale/leg matching for persisted identity. No raw ledger/history/audit/REST changes, sale-link writes, migrations, backfills, workers or transfer triggers.
Performance correction uses window ordering over recent SYSTEM postings and an inner primary-key range join to reject intervening postings, including backdated ones. A rejected correlated predecessor approach visited about 6M rows for 1,000 pairs; final measured money-flow plan visited about 172K. Regression bounds also cover 10,000 old SYSTEM postings.
Local verification (Node 22.23.3; task-owned MariaDB 11.8.6 on port 33159, database par159_test):
npm run typecheck — passed.npm run lint — passed.RUN_INTEGRATION=1 DB_HOST=127.0.0.1 DB_PORT=33159 DB_USERNAME=root DB_NAME=par159_test DB_POOL=1 npm run coverage -- --maxWorkers=2 — 459 tests / 42 files passed, including 16 bridge cases; coverage thresholds passed (43.16% lines/statements, 82.93% branches, 60.04% functions).npm run build — passed.E2E_BASE_URL=http://127.0.0.1:31159 npx playwright test test/e2e/public.spec.ts --grep 'home renders|money-flow explains|no horizontal overflow' --workers=2 — 6 passed on desktop/mobile.Task remains open for review and rollout verification. CI (including MariaDB 10.11 and full browser suite) was running when the draft opened. Production-scale latency and matching rates remain unmeasured; no shared staging/production work or deployment occurred. No product decision blocks review; maintain the distinction between aggregate estimates and authoritative sale identity.
The Explorer typecheck/lint/Vitest gate passed on CI's MariaDB 10.11. JVM build/tests, migrations, REST contract, release metadata and all four CodeQL language analyses passed. Full Explorer Playwright remains in progress at this checkpoint; live-server integration and the separate mutation job are skipped by workflow policy.
PR #85 was read back as a draft targeting develop at the intended commit. All five attachments have uploaded URLs and rendered successfully on the actual GitHub PR page. Task-owned local servers (31158/31159) and MariaDB (33159) were stopped after verification. Working tree is clean. Task stays open for review and production-scale observation; nothing was merged or deployed.
User authorized merge after readiness verification. All non-skipped CI checks passed, including full Explorer Playwright and ci-gate; no review/inline comments or conflicts were present. PR #85 merged into develop at 2026-10-22 09:27:08 UTC as b3894897add2d1312bc3176070da267cea9728e7, with verified head 67f20c8cae20f4f4ac4c3991202797e732ef15df. Production-scale latency and match-rate observation remain rollout follow-up. No manual deployment or historical data execution was performed.
Scope change: full pairing shelved, cheap fix shipped (2026-10-17)
Shipped (commit 55d4cfd, branch
par-159-implement-migration): the explorer leaves SYSTEM accounts out of the account-page counterparty list, the /me counterparty list and the richest-accounts list. Those payments still appear in transaction history. Trade-off: genuine sinks and faucets (e.g. GriefPreventionUI claim blocks) are also hidden from the counterparty widget.Shelved: collapsing bridge pairs into buyer→seller. A prototype (a
ledger_bridge_pairstable, a backfill and an AFTER INSERT trigger onledger_postings) worked, but it isn't worth a permanent trigger on the transfer hot path:Pairing rule measured on prod, if this is revisited. A SYSTEM debit pairs with the posting immediately before it on the same SYSTEM account when that posting is:
plugin_system;Vault withdraw (p)→Vault deposit (p)as the same.That rule gives 3.78M historical pairs (ChestShop 3.39M) and the backfill SELECT takes about 58 s on prod. It deliberately leaves alone BountyHunters escrow (2 s to hours apart), Essentials' fee loop (amounts differ), claim-block sinks and reward faucets. If revisited, prefer a one-time backfill plus read-time or periodic pairing over a trigger.
Still open: money-flow and volume/velocity still count bridged payments twice, for the pre-PAR-158 history and for the remaining Vault plugins.