Skip to content

feat(economy): add semantic gil telemetry module#121

Closed
Shuu-37 wants to merge 1 commit into
betafrom
feat/economy-semantic-telemetry
Closed

feat(economy): add semantic gil telemetry module#121
Shuu-37 wants to merge 1 commit into
betafrom
feat/economy-semantic-telemetry

Conversation

@Shuu-37

@Shuu-37 Shuu-37 commented Jul 13, 2026

Copy link
Copy Markdown

I affirm:

  • I understand that if I do not agree to the following points by completing the checkboxes my PR will be ignored.
  • I understand I should leave resolving conversations to the LandSandBoat team so that reviewers won't miss what was said.
  • I have read and understood the Contributing Guide and the Code of Conduct.
  • I have tested my code and the things my code has changed since the last commit in the PR and will test after any later commits.

What does this pull request do?

This draft adds semantic gil telemetry as one independently enabled Phoenix module: modules/phoenix/economy/** plus one modules/init.txt entry. It does not patch LandSandBoat src/**, scripts/**, SQL, or core settings.

The producer is disabled by default. This PR does not deploy or enable it in beta or production. The companion schema-v2 API draft is phoenix-api#62.

Architecture and data flow

authoritative Lua/native transaction -> fixed-size record -> bounded tokenized MPSC queue -> background validator/sequencer -> canonical JSON + SHA-256 -> durable gzip spool -> separate forwarder -> API/PostgreSQL

  • The map/game thread performs fixed-size copies and tokenized try_enqueue only. It performs no network, database, JSON, compression, or filesystem work.
  • Startup preallocates an 8,192-record queue and its sole explicit producer token. Tokenless/implicit producers are disabled; off-thread producer calls are rejected, counted, and reported as sequence gaps.
  • A background worker validates records, batches at most 500 combined records (and at most 100 of either control type), hashes/compresses them, and writes temp -> flush/fsync -> atomic rename -> directory fsync.
  • Default spool hard cap is 256 MiB with a 1 MiB control reserve. Queue overflow, invalid records, write failures, capacity loss, and attribution ambiguity increment counters and generate explicit controls.
  • Startup seeds a conservative map-thread watermark floor. Later quiet heartbeats advance only after OnTimeServerTick; a stalled game loop cannot manufacture continuing progress.
  • Lua overrides call super, preserve return values/errors, and synchronously unwind semantic context. With the enable flag unset/false, the Lua file returns before loading module utilities or registering overrides.
  • OnIncomingPacket is observation-only, always returns false, and never calls packet.process; core retains transaction ownership.

Instrumented transaction points

Semantic Lua before/after attribution:

  • Base addGil, delGil, setGil, confirmTrade, and tradeComplete bindings, covering all Lua callers through one module seam.
  • InteractionLookup quest/mission containers and npcUtil.completeQuest / completeMission.
  • Starting gil; GM give/take/set (including correctly scoped named targets); guild-shop buy/sell; Mug; regimes; all battlefield members; chocobo rental.
  • 38 explicitly audited transport handlers and 27 explicitly audited service handlers.
  • createShop captures stable active-NPC identity for the later native shop packet.

Version-pinned, non-consuming native correlation:

  • Normal shop buy: C2S 0x083 + wallet ITEM_NUM debit + S2C shop success.
  • Normal shop sell: C2S 0x085 + wallet credit + S2C item result; clipped mint emits applied value plus forgoneMint.
  • Bazaar: C2S 0x106 + both wallet legs + success result -> base transfer, tax burn, and any currency-cap loss.
  • Completed player trade: second locked C2S 0x033 + both wallet legs + both S2C 0x022 end results; canceled/failed trades emit no event.
  • AH listing: C2S 0x04e LotIn + wallet debit + S2C listing success -> listing-fee burn.
  • Monster gil: post-onMobDeathEx stable mob/member stage + wallet ITEM_NUM + S2C 0x029 Obtains -> correlated applied drop/forgone mint or an explicit gap.

Direct-mutation audit also covered AH bid debit, delivery escrow/claim/return, item movement, native monster distribution, shops, bazaar, and trade. AH sale remains owned by the existing replica lane to prevent double counting; delivery lacks a reliable after-commit seam. Unmatched wallet deltas become attributionGaps, never guessed other_* accounting events.

Semantic coverage matrix

Category/flow Coverage Evidence and boundary
mug Full at authoritative helper Semantic mob spawn/pool/zone + applied before/after
Normal/guild shop buy and sell Full at listed paths Semantic guild helpers; correlated native request/result/wallet proof
starting_gil, admin_grant, admin_remove Full Semantic central helper/command context; numeric actors only
regime_reward, standard battlefield_reward Full at central helpers Semantic regime/battlefield IDs + applied delta
bazaar_sale, bazaar_tax Full at completed bazaar path Both wallet legs and success must reconcile
player_trade Full at completed trade path Both participants/results reconcile; canceled/failed excluded
auction_listing_fee Full Listing success + applied wallet debit
chocobo_rental Full at central rental helper NPC/service context + applied debit
mob_drop Partial/correlated Version-sensitive death/wallet/obtains ordering; ambiguity is a gap
quest_reward, mission_reward, quest_fee Partial Central helpers/interaction containers covered; legacy leaf scripts may lack typed context
transport_fee Partial 38 audited handlers; unlisted/custom paths become gaps
service_fee Partial 27 audited handlers; unlisted/custom paths become gaps
currency_cap_loss Partial Proven bazaar/trade cap loss covered; other cap paths remain diagnostics
auction_sale Existing replica lane only Exact gross/seller is not duplicated by this producer; stable buyer attribution unavailable
delivery_box Unsupported No module after-commit seam proves escrow durability/conservation
script_reward, other_mint, other_burn, other_transfer Intentionally unsupported Unknown mutations are controls/gaps, not fabricated totals
Offline/direct database mutation Unsupported Outside the map module; reconciliation must detect supply mismatch

Performance and backpressure evidence

  • First gameplay enqueue queue allocations: 0.
  • Windows Release harness: enqueue p50 0 ns, p95 100 ns, p99 100 ns.
  • A 50,000-attempt overflow scenario remained non-blocking; 49,456 rejected records were counted and represented by an explicit queue-overflow gap in that run.
  • Simulated 80 ms map stall: heartbeat batch count remained 1 -> 1; the next map tick advanced it to 2.
  • Hard-cap scenario counted 500 capacity drops; unwritable-spool scenario counted one write failure; off-thread scenario counted/reported one producer-thread violation.
  • 16 independently decoded gzip batches passed size, checksum, sequence, batch/control-bound, atomic-completion, final-watermark, and gap assertions.
  • Live beta map-tick/CPU/memory/backlog baselines remain mandatory rollout gates; synthetic enqueue timing is not a substitute for the soak.

Test results

  • Full MSVC 19.44 RelWithDebInfo xi_map configure/build/link — passed.
  • Standalone CTest — 3/3 passed: producer/spool contract, correlator contract, Lua override contract.
  • clang-format 22.1 dry run and C++ sanity checks — passed.
  • Runtime and fixture Lua style checks; disabled-Lua guard — passed.
  • Module loader audit — exactly one runtime .lua; the test fixture uses .luatest and is not recursively executed.
  • git diff --check — passed.
  • Companion API: 36/36 focused economy tests and 26/26 fresh-migration ingest integration tests passed; both TypeScript type-checks and OpenAPI check passed.

Security and privacy considerations

  • No HTTP, network, MariaDB, or other database access exists in the module producer.
  • API credentials belong only to the separate least-privilege forwarder, never xi_map.
  • Payloads contain numeric character/content IDs and bounded canonical evidence tokens. They contain no character names, chat, or free-form script text.
  • Spool files must live outside served paths with directory mode 0750/file mode 0640 and a dedicated ownership boundary.
  • Every xi_map requires a unique producer ID and spool directory. The current flat-directory forwarder design requires one service instance per map spool.

Beta rollout sequence

  1. Review/merge phoenix-api#62 to beta, apply migration 0027, and verify API health with no producer credential or game producer enabled.
  2. Before updating the current beta host, preserve/reconcile its existing host-local helper scripts and modules/init.txt change; do not overwrite unrelated work.
  3. Review/merge this PR to beta, build, and restart beta with PHOENIX_ECONOMY_TELEMETRY_ENABLED unset/false. Verify the current single xi_map has no producer thread/spool/Lua overrides and establish disabled tick/CPU baselines.
  4. Create /var/lib/phoenix-economy/xi_map-54230/spool, owned by the game/forwarder principals with 0750 directory and 0640 files; enforce the 256 MiB budget.
  5. Install one forwarder service for the current one-map beta topology, pointed only at that spool, but keep it disabled.
  6. Create a beta-world-only economy:ingest:beta key and store it only in the forwarder environment. Configure endpoint, poll/timeout, and producer settings; keep producer and forwarder disabled.
  7. Start the forwarder first and validate idle/authentication behavior plus a synthetic spool fixture.
  8. During a maintenance restart, set the beta canary's enable flag true with unique producer ID, Phoenix commit as CONTENT_VERSION, and its spool path. Do not change production.
  9. Exercise shop, guild, bazaar, trade, AH listing, mob, quest/mission, admin, chocobo, transport, and service cases. Verify tick-backed heartbeat, checksums/sequences, ack/delete behavior, zero MariaDB telemetry QPS, and zero unexplained drops.
  10. Run a hidden seven-day soak; monitor map tick p50/p95/p99, CPU/memory, queue drops/gaps, spool/backlog, API latency, and replica-ledger reconciliation.
  11. Drill API outage, forwarder restart, duplicate retry, map restart, unwritable/full spool, burst overflow, and recovery before staff sign-off.
  12. Production remains disabled until a separate promotion decision. A future multi-map beta needs one unique spool + forwarder instance per map, or a forwarder multi-directory enhancement.

Rollback procedure

Unset/set PHOENIX_ECONOMY_TELEMETRY_ENABLED=false and gracefully restart the beta canary so it attempts a final watermark. Stop expansion; let the forwarder drain only API-acknowledged files, preserve unacknowledged spool files for diagnosis, then stop/disable the forwarder if needed. Roll back to the prior xi_map binary only if required. The additive API schema can remain; do not drop it during an incident. No gameplay MariaDB reversal is involved.

Remaining gaps and risks

  • Packet layouts and result ordering are version-pinned and need beta live-trace validation.
  • Fixed limits are 2,048 tracked characters, context depth 16, and four pending native operations per character; overflow/expiry becomes explicit gaps.
  • AH buyer identity, delivery-box exact transfer, arbitrary offline mutations, and deliberately untyped other_* events are unsupported.
  • Source catalog generation/storage, display-name mapping, source read endpoints, and dashboard drill-down are follow-up work.
  • No deterministic CPPModule::OnShutdown hook exists; atexit final watermark is best effort and crashes remain conservatively visible as unclean boots.
  • One flat spool directory cannot be shared safely by multiple xi_map processes.

Steps to test these changes

cmake -S modules/phoenix/economy -B build/economy-producer-tests
cmake --build build/economy-producer-tests --config Release
ctest --test-dir build/economy-producer-tests -C Release --output-on-failure

Also build the full xi_map target, run the repository C++/Lua formatting checks, and test once with PHOENIX_ECONOMY_TELEMETRY_ENABLED unset to confirm no Lua overrides or spool worker are activated.

@Shuu-37

Shuu-37 commented Jul 13, 2026

Copy link
Copy Markdown
Author

Superseded by #122 on the consolidated �conomy branch.

@Shuu-37 Shuu-37 closed this Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant