Skip to content

feat(autopilot-svm) PR09: settlement observation - #4754

Open
squadgazzz wants to merge 1 commit into
ilya/be-184-pr08-seam-implementations-single-auction-cyclefrom
ilya/be-185-pr09-settlement-observation
Open

feat(autopilot-svm) PR09: settlement observation#4754
squadgazzz wants to merge 1 commit into
ilya/be-184-pr08-seam-implementations-single-auction-cyclefrom
ilya/be-185-pr09-settlement-observation

Conversation

@squadgazzz

@squadgazzz squadgazzz commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Description

The loop dispatches settlements and forgets them: nothing notices whether the transaction ever landed. This PR closes that gap. The executor opens a window in solana.settlement_executions per dispatched settlement, the indexer's insert into solana.settlements fires a NOTIFY (trigger added to the schema, landing with its first listener), a ListenSession closes the window as landed with the on-chain signature, and windows whose submission deadline passes without a settlement close as timeout with an error log. A settlement observed after the timeout upgrades the window back to landed, it executed, just late, and the lateness stays visible as end_slot past deadline_slot. Windows live in the database, so a restart mid-window loses nothing: the listen seed re-checks every open window.

Why observation is indexer-driven at all: the Solana driver returns the signature at submission and landing is asynchronous, so unlike EVM there is no blocking /settle response to learn the outcome from. That also sets the column's contract apart from EVM's settlement_executions.outcome (V080): theirs records the driver's report and stays put, ours records what the indexer observed on chain, which is why a late-observed landing overwrites a timeout here and can't there. Once the competition tables exist (BE-220), timeout becomes derivable from the auction deadline the EVM way (fetch_in_flight_orders shape) and the per-cycle sweep shrinks.

Settlements are rare events, so NOTIFY fits here, unlike slot-cadence data the run loop polls per cycle. The ListenSession seed re-read covers notifies missed across reconnects: every tracked auction is re-checked whenever the listen connection (re)establishes.

Two scope notes, both marked in code: solution_uid carries the winner's driver-local solution id until competition persistence (BE-220) allocates uids, and the timeout check runs per competition cycle, so on an idle chain a miss surfaces with the next auction rather than at its deadline slot.

Changes

  • solana.settlements gains an AFTER INSERT trigger notifying solana_settlement_finalized with the auction id (V2 edited in place, the series is deployed nowhere)
  • infra/observation.rs: opens, lands, and times out solana.settlement_executions windows, with the NotifyHandler resolving notifies and reconnect re-reads against solana.settlements
  • The executor registers each dispatch, the observer drives the deadline check off the per-cycle tip

How to test

New DB-backed tests driving the full path (INSERT fires the trigger, the listener closes the window as landed with the signature) and the deadline expiry. The mock-cycle test now also asserts the dispatch opens a window.

@linear-code

linear-code Bot commented Aug 18, 2026

Copy link
Copy Markdown

BE-185

@squadgazzz
squadgazzz marked this pull request as ready for review August 19, 2026 08:25
@squadgazzz
squadgazzz requested a review from a team as a code owner August 19, 2026 08:25
@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @squadgazzz's task in 2m 50s —— View job


PR Review: settlement observation

The design is clean — windows persisted in the DB, NOTIFY + reconnect re-seed for convergence, and timeouts upgraded to landed when a settlement lands late. The ListenSession reuse and the best-effort window-open in the executor are nicely reasoned. Two points I'd like addressed, posted inline:

  • observer.rs:45flag_expired(*tip).await? propagates through persist_competition_ranking, which run_loop::single_run gates dispatch on. A transient DB error in the (unrelated) timeout bookkeeping would abort dispatch of the current winner. This contradicts the executor's own "window open must not block the settlement" principle; make it best-effort (log & continue).
  • observation.rs:157-175resolve/landed collapse all of an auction's windows onto a single settlement's signature and don't key on solver. Correct for one winner, but max_winners is configurable and the executor opens one window per winner, so multi-winner silently mis-attributes. Worth keying by solver or at least logging a count mismatch.

Nothing blocking beyond the first item; the second is a heads-up for when multi-winner arrives.

  • Read the PR diff and changed files
  • Analyze observation.rs, executor.rs, observer.rs
  • Review DB migration / trigger
  • Post inline findings and summary

Comment thread crates/autopilot-svm/src/infra/observer.rs Outdated
Comment thread crates/autopilot-svm/src/infra/observation.rs
@squadgazzz
squadgazzz force-pushed the ilya/be-184-pr08-seam-implementations-single-auction-cycle branch from 8f29d7e to 5c1b874 Compare August 19, 2026 11:14
@squadgazzz
squadgazzz force-pushed the ilya/be-185-pr09-settlement-observation branch from 03436e2 to 760fcae Compare August 19, 2026 11:15
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