Skip to content

feat(hyperliquid): recover settled outcomes' question_id from settledOutcome - #306

Merged
0237h merged 1 commit into
mainfrom
feat/outcomes-settled-question-grouping
Jun 16, 2026
Merged

feat(hyperliquid): recover settled outcomes' question_id from settledOutcome#306
0237h merged 1 commit into
mainfrom
feat/outcomes-settled-question-grouping

Conversation

@0237h

@0237h 0237h commented Jun 16, 2026

Copy link
Copy Markdown

Summary

When a multi-outcome question fully resolves (e.g. "World Cup: Germany vs Curacao"), HL drops it from `outcomeMeta`. The live `outcomeToQuestion` map then can't link the settled child outcomes back to their parent, so they land in `state_outcome_meta` with `question_id=null` and downstream consumers lose the grouping.

HL still exposes the link on the `settledOutcome` response under `question.question.{settled|live}`. This change:

  • Extends `HyperliquidSettledOutcome` to type the optional `question` wrapper and adds `extractSettledQuestionId` as the extraction helper.
  • Falls back to that extractor in `outcomes.ts` when the live map has no entry — covering both lifecycle states (parent still active vs fully settled).
  • Narrows the already-settled guard to `status='settled' AND question_id IS NOT NULL`. Previously-orphaned rows get re-probed once on the next cycle and self-heal via the RMT collapse.

Binary single-outcome markets continue to re-probe each cycle (no parent to find, write is idempotent) — small steady-state cost in exchange for not adding a separate "probed completely" sentinel to the schema.


🤖 Generated with Claude Code

…Outcome

`outcomeMeta` drops a multi-outcome question once it fully resolves, so
the live `outcomeToQuestion` map can no longer link the settled child
outcomes back to their parent question. The settled outcomes ended up
in `state_outcome_meta` with `question_id=null`, and downstream
consumers lost the ability to group them under their parent market.

HL still exposes the link on the `settledOutcome` payload:

    response.question = {
        question: { settled: <qid> } | { live: <qid> },
        name: "...",
        description: "...",
    }

Adds `extractSettledQuestionId(settled)` to read the discriminated
field and uses it as a fallback when the live map has no entry. The
two paths cover both lifecycle states:
- parent question still active (partial settlement): live map hits
- parent question fully settled and dropped: response.question hits

Self-healing for previously-orphaned rows: the already-settled guard
now requires `question_id IS NOT NULL`, so settled outcomes captured
before this change get re-probed on the next cycle and have their
parent question id backfilled via the RMT collapse. Binary
single-outcome markets re-probe each cycle (no parent to find, write
is idempotent) — a small steady-state cost worth the simpler logic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the Hyperliquid outcomes scraper so that settled child outcomes from fully-resolved multi-outcome questions can still be linked back to their parent question_id, even after Hyperliquid drops the parent question from outcomeMeta.

Changes:

  • Add extractSettledQuestionId() and extend the settledOutcome type to capture the optional question wrapper for parent question linkage.
  • Fall back to extracting question_id from settledOutcome when the live outcomeToQuestion map has no entry.
  • Narrow the “already settled” skip set to status='settled' AND question_id IS NOT NULL, so previously-orphaned rows can be re-probed and backfilled.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
services/hyperliquid/outcomes.ts Uses extractSettledQuestionId() fallback and adjusts “already settled” filtering to allow orphan backfill.
services/hyperliquid/outcomes.test.ts Adds an integration-style cycle test to verify question_id recovery from settledOutcome.
services/hyperliquid/outcomes-info.ts Extends settled outcome typing and adds extractSettledQuestionId() helper.
services/hyperliquid/outcomes-info.test.ts Adds unit tests covering extractSettledQuestionId() behavior across wrapper variants.

Comment on lines +47 to +53
* Discover outcome_ids we've already captured as `status='settled'` *with* a
* populated `question_id`. Once both are written the row is terminal — settled
* payloads are immutable on the HL side, so skipping them turns the steady-state
* cycle into a no-op on the Info API even as the cumulative settled universe
* grows. Rows with `status='settled' AND question_id IS NULL` are orphans
* (early scraper missed the multi-outcome question link) and stay eligible for
* re-probe so they self-heal on the next cycle.
Comment on lines 55 to 58
async function fetchAlreadySettledIds(): Promise<Set<number>> {
const { data } = await query<{ outcome_id: string }>(
"SELECT toString(outcome_id) AS outcome_id FROM state_outcome_meta FINAL WHERE status = 'settled'",
"SELECT toString(outcome_id) AS outcome_id FROM state_outcome_meta FINAL WHERE status = 'settled' AND question_id IS NOT NULL",
);
@0237h
0237h merged commit 07163dd into main Jun 16, 2026
2 checks passed
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.

2 participants