Instead of fetching up to 50k inventory snapshots (~442MB), scope the query to only the time range of the current submission batch with a 15-minute buffer. For a typical 1-hour batch this drops from 50k rows to ~300 rows, well within the 1280Mi pod memory limit. The coalesced_at_idx on intent_inventory_snapshots covers the BETWEEN clause so this remains efficient. Proof: history-writer OOM kills from refreshQuoteOutcomes inventory fetch Assumptions: 15min buffer covers the attribution window for all submissions Still fake: heuristic gap outcomes may attribute trades imprecisely
8.6 KiB
Implementation Turn: pre-credit funding visibility and operator alerts
Status: open Opened: 2026-04-02
Goal
Make the already-live BTC/EURe loop operationally understandable between external funding and spendable credit, and make critical stale or failed states queryable as durable alert records instead of transient logs.
Selected backlog items
- [O003] Alerts for stale reference prices, stale inventory state, stuck funding actions, and failed executor submissions.
- [O004] Pre-credit funding visibility for slow chains: watch configured deposit addresses at chain level, track inbound transfers through mempool and on-chain confirmation before bridge credit, persist that state separately from spendable inventory, and alert operators when funding is seen, delayed, or stuck.
Design rule
Keep the already-proven execution and inventory truth path intact:
- verifier and bridge credit remain the only spendable truth
- pre-credit visibility is additive observability
- alerts are additive operability
Also keep the future operator architecture clean:
- no browser-direct Kafka or Redpanda dependency
- future dashboard reads PostgreSQL for history and service HTTP state for live controls and freshness
- this turn should therefore emit stable records and machine-friendly current-state summaries rather than log-only clues
Event backbone
Retain Kafka as the backbone. Add only the minimal new topics required:
ops.funding_observationops.alert
These topics are append-only evidence streams, not control planes.
Durable store
Extend PostgreSQL with new append-only families:
funding_observationsops_alerts
If a current-state materialization is needed, derive it from append-only records or keep it as a clearly named snapshot table. Do not replace the append-only record.
Service changes
1. liquidity-manager
Extend the existing treasury owner instead of inventing a broad new funding stack.
New responsibilities:
- retain active funding handles by chain and asset
- poll configured chain observers for those handles
- emit
ops.funding_observation - correlate chain observations with bridge
recent_deposits - expose latest observations and credit-correlation state through
/state
Expected state shape additions:
funding_observations_by_handlelatest_funding_observation_atuncredited_funding_total_by_assetcredit_correlationobserver_healthobserver_age_msor equivalent freshness signal
Control additions:
POST /refresh-funding-observations- optional
POST /pause-funding-observer - optional
POST /resume-funding-observer
Important implementation constraints:
- do not change withdrawal behavior
- do not reuse spendable inventory fields for pre-credit state
- keep BTC and EURe observation records in one shared schema
2. inventory-sync
Keep current spendable accounting intact.
Possible additions:
- read the latest funding observations
- expose a separate
pre_credit_inboundorfunding_visibilityfield in/state
Hard rule:
spendable,pending_inbound, and strategy-facing credited truth must not become looser
3. history-writer
Consume and persist the new topics:
ops.funding_observationops.alert
Expose through /state:
- latest funding-observation write time
- latest alert write time
- counts or offsets for the new topics
Add query-friendly indexes for:
tx_hashfunding_handlealert_codestatusasset_idchainingested_at
If this turn needs a current-state SQL materialization for future operator queries, keep it narrow and explicit, for example:
- latest funding observation per handle
- currently active alerts
Do not replace append-only records with mutable-only state.
4. ops-sentinel or equivalent alert evaluator
Add one small service only if needed to keep alert logic separate and testable.
Responsibilities:
- consume:
ref.market_pricestate.intent_inventoryops.liquidity_actionops.funding_observationexec.trade_result
- evaluate policy windows for stale and stuck conditions
- emit
ops.alertraise/clear transitions - expose current alert state
Preferred alert model:
- stable
alert_code status:raisedorclearedseverityreasonfirst_raised_atlast_evaluated_at- correlation IDs when available
State shape should be future-dashboard-friendly:
active_alertsrecent_transitionslast_evaluated_atstalepaused
Minimal alert set for this turn:
reference_price_staleinventory_snapshot_stalefunding_seen_unconfirmedfunding_confirmed_credit_pendingfunding_stuckexecutor_submission_failed
Do not add Slack, email, or paging integrations in this turn unless required to prove the path. Durable alert records plus HTTP state are sufficient.
Chain observer plan
BTC
Must be the first-class proof path.
Implementation expectations:
- configurable observer endpoint
- look up the configured BTC deposit address
- detect:
- mempool appearance when available
- confirmation count
- credited transition once bridge/verifier catches up
Assumption to keep explicit in code:
- a chain observer can disappear or lag independently of the bridge
Gnosis / EURe
Nice-to-have within the same schema, but BTC is the proof-critical path.
If included this turn:
- watch the configured deposit address for EURe token transfers
- represent observation state in the same event model
Record shapes
ops.funding_observation
Required fields:
funding_observation_idaccount_idasset_idchainfunding_handlesourcetx_hashstatusamountconfirmationsfirst_seen_atlast_seen_atcredited_atwhen knownbridge_deposit_tx_hashwhen correlated
ops.alert
Required fields:
alert_event_idalert_codestatusseverityreasonservice_scopepairwhen relevantasset_idwhen relevanttx_hashwhen relevantraised_atcleared_atdetails
Control surface expectations
liquidity-manager
Must expose:
- active deposit handles
- latest pre-credit funding observations
- latest credit correlation
- whether the funding observer is healthy or paused
- explicit ages or timestamps so the future top bar can mark stale data without inferring it from logs
alert evaluator
Must expose:
- current active alerts
- latest cleared alerts
- per-alert evaluation timestamps
- pause state
- enough stable fields that a future dashboard backend can render alert lists and severity without custom log parsing
history-writer
Must expose the new topic offsets and write status for funding observations and alerts.
Tests
Required automated coverage:
- BTC funding observation remains non-spendable before credit
- alert transitions raise then clear on recovered stale state
- funding observation correlates to a later credited deposit without losing the original tx hash
- executor failure produces an alert event
If a meaningful automated test cannot be written for a subpath, stop and record why instead of hand-waving.
Validation plan
- Safe induced stale-price alert:
- pause
market-reference-ingest - wait past freshness window
- observe
reference_price_stale - resume and observe clear
- pause
- Safe induced stale-inventory alert:
- pause
inventory-sync - wait past freshness window
- observe
inventory_snapshot_stale - resume and observe clear
- pause
- Funding visibility proof:
- use a real deposit address
- observe pre-credit chain state before bridge credit where timing allows
- later observe credit correlation
- Executor failure alert proof:
- use a controlled non-destructive failure mode such as temporary relay endpoint override in a safe environment or a replayable failure fixture
- verify
executor_submission_failed
Future dashboard handoff
This turn should leave the next dashboard turn with these ready-made data sources:
- PostgreSQL history for funding observations and alerts
- service
/statefor current funding status, current alert status, balances, arming state, and freshness - stable timestamps and reason fields that can drive:
- a top status bar
- a funds page
- a system alert list
The next dashboard turn should not need to invent interpretation logic that belongs in this turn's services.
Out of scope on purpose
- No new trading strategy
- No historical backtest engine
- No broad observability stack
- No polished dashboard frontend
- No automated treasury refills
Still fake at turn open
- Pre-credit funding visibility is still missing from the live cluster.
- Alert state is still mostly implicit in service logs and manual inspection.
- There is no durable operator-facing record yet for "funds are on the way but not spendable."