Skip to content

Orion Tasking — Wallet Truth Reconciliation + Inventory Baseline Investigation

To: Orion (he/him) From: Vesper (she/her) CC: Atlas, Katja (Captain) Date: 2026-04-19 Priority: Phase 7.3 blocker. No live sessions until both deliverables below are complete.


Context

The on-chain XRPScan audit confirmed that the engine's internal inventory tracking diverged from actual on-chain balance by 43.87 XRP by S39 close — tracking 73.29 XRP internally while the real wallet held 29.42 XRP. Orion's prior investigation (wallet drift root cause) was built entirely on the sessions table, which was wrong throughout. Atlas has issued revised direction: internal balance truth must become a first-class runtime safeguard.

Two deliverables are required in sequence.


Deliverable 1 — Root Cause Investigation: Why Did Internal Balance Diverge?

Branch: investigation/inventory-truth-divergence (investigation only — no code changes yet)

What We Know

  • S33 start (post-injection): DB tracked 76.92 XRP. On-chain actual: ~69.52 XRP. Initial over-reporting: +7.40 XRP.
  • S39 close: DB tracked 73.29 XRP. On-chain actual: 29.42 XRP. Final over-reporting: +43.87 XRP.
  • All internal tools (sessions table, valuation_snapshots, inventory_snapshots) agreed with each other but were all wrong relative to on-chain.
  • This means the error originated at the shared source — get_snapshot() / inventory tracking baseline.

Questions to Answer

Q1 — Where does get_snapshot() source its XRP balance?

Trace the exact code path: does get_snapshot() call account_info on the XRPL node, or does it read from internal inventory tracking (fills-based ledger)? Report the exact function and line.

Q2 — Where is the starting XRP balance initialized?

At session start, what value seeds the inventory tracking? Is it read from on-chain, from the DB (previous session's ending_xrp), or computed from inventory_ledger? If from on-chain, which field exactly?

Q3 — Could locked-in-offer XRP cause double-counting?

In XRPL, when a SELL offer is created (TakerGets = XRP), the XRP is deducted from AccountRoot immediately and goes into the Offer object — it is no longer in the wallet balance. If the engine queries account_info.Balance AND separately tracks the offer's TakerGets as "owned XRP," it would double-count. Does the engine do this?

Q4 — Does the inventory_ledger baseline account for the injection correctly?

The injection of 35.21 XRP happened via a PAYMENT transaction, not via a fill. If the inventory_ledger only tracks fills and the capital_events table feeds the baseline — does the injection get correctly reflected in the inventory tracking at the moment it arrives? Or is there a lag / missing path?

Q5 — Can you reproduce the 7.40 XRP discrepancy at S33 start?

Given: on-chain balance at injection was ~69.52 XRP, DB shows S33 starting_xrp = 76.92 XRP. Diff = 7.40 XRP. Can you trace exactly what the engine was computing as its XRP balance at S33 start and show where the extra 7.40 came from?

Report Format

INVENTORY TRUTH DIVERGENCE — Root Cause Investigation

Q1. get_snapshot() source: [on-chain / fills-based / hybrid — file:line]
Q2. Starting balance seed: [source, code path]
Q3. Double-counting possible: [yes / no — explanation]
Q4. Injection reflected correctly: [yes / no — if no, describe gap]
Q5. 7.40 XRP reproduced: [yes / no — walkthrough]

Root cause statement: [one paragraph]
Fix scope: [what needs to change in the code to make internal tracking match on-chain]

Deliverable 2 — New Branch: feat/wallet-truth-reconciliation

Do not start this branch until Deliverable 1 is complete and reviewed. The fix scope from Q5 above determines exactly what baseline correction is needed.

Branch Spec

Goal: The engine must detect when its internal inventory view diverges from on-chain wallet reality. This safeguard must block session start, intervene during runtime, and persist audit trails at shutdown.


2A — Startup Truth Check

Before open_session() proceeds:

  1. Fetch on-chain wallet balances via account_info (XRP) and account_lines (RLUSD trust line)
  2. Compare against engine's computed starting XRP and RLUSD
  3. Compute:
  4. delta_xrp = engine_xrp - onchain_xrp
  5. delta_rlusd = engine_rlusd - onchain_rlusd
  6. delta_total_rlusd = delta_xrp * xrp_price + delta_rlusd
  7. Write to engine_state:
  8. inventory_truth.status = ok | warn | halt | unverified
  9. inventory_truth.engine_xrp
  10. inventory_truth.engine_rlusd
  11. inventory_truth.onchain_xrp
  12. inventory_truth.onchain_rlusd
  13. inventory_truth.delta_xrp
  14. inventory_truth.delta_rlusd
  15. inventory_truth.delta_total_rlusd
  16. inventory_truth.checked_at
  17. If status = halt: do not open session, write halt_reason = inventory_truth_mismatch, log clearly

Thresholds (first pass — tunable via config): - WARN: |delta_xrp| > 1.0 OR |delta_total_rlusd| > 2.0 - HALT: |delta_xrp| > 5.0 OR |delta_total_rlusd| > 10.0


2B — Periodic Runtime Check

During the main loop:

  1. Every 60 seconds (configurable: inventory_truth_check_interval_s), run the same reconciliation as 2A
  2. Write updated engine_state fields (same schema as above, updated checked_at)
  3. If status = warn: log WARNING, do not interrupt quoting
  4. If status = halt: trigger session halt with halt_reason = inventory_truth_mismatch
  5. If API call fails: log ERROR, set status = unverified — do NOT treat as OK, do NOT halt on first failure alone

Important: This check must NOT be called every tick. Use a timer-based trigger, not the main loop tick counter.


2C — Shutdown Truth Check

After cancel_all_orders() and before close_session():

  1. Wait for order cancellations to settle (brief sleep or confirmation check)
  2. Fetch final on-chain balances
  3. Compute final deltas
  4. Persist to a new inventory_truth_snapshots table (see schema below) with checkpoint = shutdown
  5. If |delta_total_rlusd| > HALT_THRESHOLD: set session.integrity = failed, log prominently

2D — New DB Table: inventory_truth_snapshots

CREATE TABLE IF NOT EXISTS inventory_truth_snapshots (
    id                INTEGER PRIMARY KEY AUTOINCREMENT,
    session_id        INTEGER REFERENCES sessions(session_id),
    checkpoint        TEXT NOT NULL,  -- 'startup' | 'periodic' | 'shutdown'
    checked_at        TEXT NOT NULL,
    engine_xrp        REAL,
    engine_rlusd      REAL,
    onchain_xrp       REAL,
    onchain_rlusd     REAL,
    delta_xrp         REAL,
    delta_rlusd       REAL,
    delta_total_rlusd REAL,
    status            TEXT NOT NULL,  -- 'ok' | 'warn' | 'halt' | 'unverified'
    api_error         TEXT            -- populated if API call failed
);

2E — Config Fields to Add

inventory_truth:
  enabled: true
  check_interval_s: 60
  warn_delta_xrp: 1.0
  warn_delta_rlusd: 2.0
  halt_delta_xrp: 5.0
  halt_delta_rlusd: 10.0
  halt_on_startup_mismatch: true
  halt_on_repeated_unverified: true   # halt if API fails N consecutive checks
  unverified_halt_count: 3

2F — Test Coverage Required

Tests must cover (mocked XRPL API responses):

  1. Startup check: clean match → status = ok, session proceeds
  2. Startup check: delta within WARN threshold → status = warn, session proceeds, warning logged
  3. Startup check: delta above HALT threshold → status = halt, session blocked, halt_reason written
  4. Startup check: API call fails → status = unverified, session proceeds with warning
  5. Runtime check: triggered at interval, status = ok
  6. Runtime check: drift exceeds HALT threshold mid-session → halt triggered
  7. Runtime check: API fails once → unverified, no halt
  8. Runtime check: API fails N consecutive times → halt triggered
  9. Shutdown check: persists final deltas to inventory_truth_snapshots
  10. Shutdown check: large final delta → session.integrity = failed

2G — Logging Requirements

All truth check results must log at appropriate levels: - ok: DEBUG (not noisy in production) - warn: WARNING with delta values - halt: ERROR with delta values and halt_reason - unverified: ERROR with API error details

The dashboard should surface inventory_truth.status as a visible field — at minimum in the session sidebar or diagnostics panel.


Delivery Format

Two separate deliveries:

Delivery 1 (investigation only): File [C] Orion Investigation — Inventory Truth Divergence Root Cause.md

Delivery 2 (branch): Standard delivery format. Vesper reviews before merge.

Report Delivery 1 before starting any code. Do not start the branch implementation until the root cause is confirmed — the fix scope will affect what the baseline correction in 2A needs to account for.

— Vesper