Currency top-holders / per-account balances exporter (deferred from #53) #58
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Split out from #53 (currency & money-supply surface) per the AGENTS.md pull-everything rule.
Gap
get_eco_currencymeets DiscordLink'sCurrenciesand most ofCurrency <name>from today's export surface, but the top-holders list inCurrency <name>is not reachable. No current endpoint carries per-account currency balances:PersonalWealthInDefaultCurrency/GovernmentHoldingsInDefaultCurrencyare single aggregate series in the default currency, not per-account and not per-currency.CurrencyTraderows give buyer/seller flows, not balances, and the numeric buyer/seller ids still hit the id-to-name join blocker (#5).The per-currency report renders a
holders unavailablenote pointing here rather than faking a list.What this needs
A reset-gated stores/economy exporter mod that reads
CurrencyManagerholdings in-process and exports, per currency, the top account balances (joined to names via the sameUserManageraccess the jobs mod already uses for/api/v1/citizens). Once that surface exists, wire it intocurrency.py's per-currency report and drop the deferred note.Dependencies
Director surface (read-only) - scope for the engineer run.
Headless scope: author the reset-gated stores/economy exporter that reads
CurrencyManagerholdings in-process and exports, per currency, the per-account balances (the top-holders listget_eco_currencycurrently renders as "holders unavailable"). Follow the existing patterns inmods/stores/src(the store exporter builds green today).Done-condition:
dotnet build mods/stores/src/EcoStoreExporter.csprojgreen onmain.Out of scope for this run (Kai's hands, live): the reset-gated deploy to kai-server + on-cycle verify. Note the buyer/seller numeric ids still depend on the citizen id->name join (#65) being live to render names rather than
Citizen #<id>.🔒 Reserved by
ward agent --driver claude— containerengineer-claude-eco-app-58on hostKAI-DESKTOP-TOWERis carrying this issue (reserved 2026-07-06T03:15:18Z). Concurrentward agentruns are blocked until it finishes or the reservation goes stale (2h0m0s TTL);--forceoverrides.Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).
run seed context — what this run is carrying (ward#609)
coilyco-gaming/eco-app#58· branchissue-58· driverclaude· workflowdirect-mainengineer-claude-eco-app-58· wardv0.413.0· dispatched2026-07-06T03:15:18ZIssue body as seeded:
Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.413.0).
— Claude (she/her), via
ward agentWARD-OUTCOME: done - live per-currency top-holders exporter (
/api/v1/currency-holdings) shipped in mods/stores and wired into theCurrency <name>report, deferred note dropped, landed on main.Honestly this one went smoother than I expected, and the reason is that the repo had already built the exact pattern I needed twice over. The stores mod's reflection-by-name
StoreScannerand the jobs mod's typedUserManagerjoin were a ready-made template: one stable typed touchpoint, everything drifty read by name, skip-and-continue on nulls. So the shape of the C# side wrote itself. I extracted the shared reflection primitives into aReflect.csso the newCurrencyHoldingsScannerandStoreScannerstop duplicating them.The part that genuinely fought back was guessing the Eco economy API blind. My first pass assumed
CurrencyManager.Currencies+ aBankAccountManager.Objsingleton andaccount.CurrencyHoldingsas a flat list. It compiled — reference-assembly builds happily compile against wrong guesses — but that's exactly the trap: a green compile gate says nothing about whether reflection resolves at runtime. So I loaded the realEco.ReferenceAssembliesthrough aMetadataLoadContextprobe and checked every member. Two would have silently returned empty on a live server:CurrencyHoldingsis aControllerDictionary(enumerating it yields KeyValuePairs, not holdings — I now read.Values), and the owner isAccountOwner, notOwner. The singleton.Objalso neededFlattenHierarchyto resolve through theSingleton<T>base. Glad I checked rather than trusting the build.Confidence: high on the Python wiring and DTO contract (fully unit-tested end-to-end against the shell harness shape, both reachable and mod-undeployed paths). Medium-high on the live C# walk — every member name is verified against 0.13.0.4-1024 metadata and the shell endpoint serializes correctly, but I could not exercise the reflection against a running server from here, so the true proof is the first live deploy. The design fails safe (empty list, never a 500), and the Python side degrades to the unavailable note if the mod isn't deployed, so a wrong guess is visible, not silent.
Rough edges / follow-ups worth filing: (1) the SPA currency strip carries the new
topHoldersfields on its type but doesn't render them yet — a natural/tradeenhancement. (2) Owner attribution is single-owner only; government/company accounts show the account name with a null holder, which is correct but coarser than a per-member split. (3) Worth a quick sanity pass on the live server once the DLL lands to confirm the account-enumeration strategy picksBankAccountManager.Obj.Accounts.