Skip to content

Vesper Ruling — Wallet Truth Reconciliation D2 Blockers

To: Orion (he/him) From: Vesper (she/her) CC: Atlas, Katja (Captain) Date: 2026-04-19 Re: D1 accepted. Rulings on four blocking questions. D2 green light at end.


D1 Accepted

Root cause is confirmed and reproduced to within rounding. The structural framing is correct: one cold-start read, never refreshed. Primary driver: reconciler phantom-fill heuristic. Secondary: zero-qty fill dust. The 7.40 XRP gap reconstructed exactly from first principles. Accepted without further questions.


Ruling 1 — Threshold Defaults

Use the spec defaults. Do not tighten.

WARN: |delta_xrp| > 1.0 OR |delta_total_rlusd| > 2.0 HALT: |delta_xrp| > 5.0 OR |delta_total_rlusd| > 10.0

Reasoning: these thresholds are for detecting NEW divergence after the baseline is corrected. The phantom-fill bug accumulated 6.71 XRP across S1–S32 (~750 fills, ~0.009 XRP per fill average). At 1.0 XRP warn, a new drift episode would be flagged within roughly the first 100 fills — early enough to catch before it compounds. Tightening to 0.5 / 2.0 risks alert fatigue from normal rounding and node-snapshot variance on a small portfolio. Spec defaults stand.

Both WARN and HALT thresholds must be config-tunable per the spec (warn_delta_xrp, halt_delta_xrp, etc.). We can tighten in production once we have data on normal variance.


Ruling 2 — API Failure Policy

unverified_halt_count = 3 is the production value.

Reasoning: account_info RPC is indeed reliable on standard XRPL public nodes. Three consecutive failures means something is genuinely wrong with connectivity or the node, not transient jitter. A single failure sets status = unverified and logs ERROR but does not halt. Two failures in a row escalate logging. Three triggers halt.

One important addition to the spec: the three-consecutive-failure counter must reset on any successful verification. A transient blip followed by recovery should not accumulate toward halt. Implement as consecutive_unverified_count that resets to zero on status = ok or status = warn.


Ruling 3 — Backfill Strategy for the Existing 43.87 XRP Gap

The branch must ship with a one-shot realignment tool. Manual DB edits are not acceptable.

When feat/wallet-truth-reconciliation lands and the first startup truth check runs, it will see the 43.87 XRP gap and halt immediately. This is correct behavior. But we need a clear, auditable remediation path — not manual SQL.

Required: tools/realign_inventory_to_onchain.py

This standalone script (not part of the engine runtime) must:

  1. Connect to neo_engine.db and the XRPL node (reads config from standard config file)
  2. Fetch on-chain account_info.Balance (XRP) and account_lines (RLUSD) — live query
  3. Compute internal engine balance the same way rebuild() would: fills_only_ledger_tip + capital_overlay
  4. Print a dry-run summary:
    On-chain XRP:     29.42
    Internal XRP:     73.29
    Delta XRP:        -43.87  ← WILL WRITE AS REALIGNMENT
    On-chain RLUSD:   154.46
    Internal RLUSD:   154.46
    Delta RLUSD:      0.00
    
  5. Require explicit --confirm flag to proceed
  6. On confirm: write a capital_events row with event_type='realignment', asset='XRP', amount=-43.87, a comment field noting this is a one-time inventory reconciliation to on-chain truth
  7. Log clearly to stdout and to a timestamped file in logs/

After running the tool, the engine's next rebuild() will incorporate the realignment entry in the capital overlay, making internal state match on-chain. The startup truth check will then pass.

The tool must NOT be runnable while the engine is live. Add a lock check: if engine_state contains an active session (started_at but no ended_at), refuse with an error.

WAC implications (Q4 preempts here): after the realignment entry is written, WAC will still be wrong until the phantom-fill fix lands. That is acceptable — WAC is a display metric, not a safety gate. The truth check does not use WAC.


Ruling 4 — WAC Correction

Out of scope for feat/wallet-truth-reconciliation. Flagged separately.

The WAC distortion is real: _rebuild_wac replays from fills, which includes phantom fills. Fixing WAC correctly requires first eliminating the phantom-fill source (FLAG-037 / fix/reconciler-disappeared-order-conservative). Once that branch lands, a WAC rebuild pass can follow. Attempting WAC correction before the phantom-fill source is patched would produce a corrected-but-still-drifting WAC.

This is filed as FLAG-040. Not a gate for D2.


D2 Green Light

All four blockers are ruled. Orion is cleared to start feat/wallet-truth-reconciliation.

Scope is exactly as specified in the tasking, with these addenda:

  1. consecutive_unverified_count resets on successful verification (Ruling 2)
  2. tools/realign_inventory_to_onchain.py must ship as part of this branch (Ruling 3)
  3. WAC correction is explicitly out of scope (Ruling 4)
  4. Thresholds per spec defaults (Ruling 1)

Deliver in standard format. Vesper reviews before merge.

Reminder: the phantom-fill root cause (ledger_reconciler.py:675–687) is intentionally NOT in this branch. The detector lands first. The fix to what the detector will catch lands in fix/reconciler-disappeared-order-conservative (FLAG-037). That sequence is correct.

— Vesper