Skip to content

mostlyright.core.schemas.earnings_fact

Earnings-fact schema (schema.earnings_fact.v1) — Phase 27 (27-01).

One row per (ticker, call_id, term, occurrence) mention counted from an earnings call. This is the settlement-flipping load-bearing contract: the two venues (Kalshi, Polymarket) resolve the SAME fact rows through different filters (see 27-RESEARCH-MARKETS-RULES-STT.md §2), so the schema must carry every taxonomy dimension both venues key on.

Two surfaces live here:

validate_kalshi_counted_occurrence : The fail-closed Kalshi-count hook (D-27.11). A Kalshi-counted occurrence MUST carry role_source ∈ {transcript_structural, transcript_self_id, roster_match} AND speaker_role ∈ {company_executive, operator_ir}. Un-anchorable occurrences are EXCLUDED from the Kalshi count (recall loss accepted to avoid mis-settlement) but RETAINED for the Polymarket any-speaker count. ColumnSpec carries no regex/cross-field rule (core/schema.py), so this is a module-level hook mirroring validate_satellite_station.

validate_ticker : The ticker-identity hook, raising EarningsFactCorruptError for a malformed ticker — mirrors validate_satellite_station.

EarningsFactSchema : The full schema.earnings_fact.v1 shape contract.

delivery vs source (D-27.2/D-27.6, satellite precedent). delivery (enum {live, hosted}, default "live") is INFORMATIONAL LINEAGE ONLY — the delivery channel, NOT source identity. The per-row source overlay column is the validator-required source-identity column. Both the live STT path and the hosted feed produce rows from the SAME upstream public call audio, so they share one source identity ("earnings_call"), exactly as satellite’s noaa_goes.

Streaming fields (27-10 Task 1, additive). resolution_status carries the extra "provisional" value for live early-signal fact deltas, and the is_final / spoken_at markers are ADDITIVE/optional so a batch (post-call) row is unchanged. is_final denotes STT-segment finality ONLY (D-27.14/ D-27.16) — a counted fact delta is is_final=True yet still resolution_status="provisional". Settlement/backtest authority is gated on resolution_status / source ONLY, never on is_final; validate_kalshi_counted_occurrence is mode-agnostic (applies identically to live and batch).

DELIVERY_VALUESDelivery-channel lineage (D-27.2/D-27.6).
ROLE_SOURCE_VALUESD-27.11 role_source — the provenance of the speaker-role attribution.
SPEAKER_ROLE_VALUESSpeaker role of the occurrence (RESEARCH-MARKETS §2.1(4)).
COUNTING_MODE_VALUESCounting mode (RESEARCH-MARKETS §2.1(2)).
SEGMENT_VALUESCall segment of the occurrence (RESEARCH-MARKETS §2.1(5)).
WINDOW_SCOPE_VALUESTime-window scope a market resolves over (RESEARCH-MARKETS §2.1(5)).
MATCH_RULE_VALUESTerm match rule (RESEARCH-MARKETS §2.1(3)).
COMPOUND_TYPE_VALUESCompound-type axis (D-30; RESEARCH-MARKETS §2.1(3) “Compound words — REAL CROSS-VENUE DIVERGENCE” + §2.2 “Closed-compound divergence”).
KALSHI_AUTOCOUNT_COMPOUND_TYPESVenue auto-count membership over COMPOUND_TYPE_VALUES (D-30).
RESOLUTION_STATUS_VALUESResolution status (RESEARCH-MARKETS §2.1(6)).
KALSHI_COUNTABLE_ROLE_SOURCESD-27.11 fail-closed membership sets.
validate_kalshi_counted_occurrence(…[, strict])Fail-closed Kalshi-count rule (D-27.11).
validate_kalshi_counted_rows(rows, *[, strict])Cross-field guard: a stored kalshi_counted=True MUST be derivable.
validate_ticker(ticker)Validate an earnings-fact row’s ticker identity.
EarningsFactSchema()schema.earnings_fact.v1 — one row per counted mention occurrence.

mostlyright.core.schemas.earnings_fact.COMPOUND_TYPE_VALUES : tuple[str, …] = (‘standalone’, ‘open’, ‘hyphenated’, ‘closed’, ‘affix_derivation’)

Section titled “mostlyright.core.schemas.earnings_fact.COMPOUND_TYPE_VALUES : tuple[str, …] = (‘standalone’, ‘open’, ‘hyphenated’, ‘closed’, ‘affix_derivation’)”

Compound-type axis (D-30; RESEARCH-MARKETS §2.1(3) “Compound words — REAL CROSS-VENUE DIVERGENCE” + §2.2 “Closed-compound divergence”). This is a SEPARATE per-occurrence axis from MATCH_RULE_VALUES — do NOT overload the match-rule enum with these values. A hyphenated compound counts for the bare term on BOTH venues; a closed (unhyphenated) compound (wildfire for fire) diverges: Kalshi No, Polymarket candidate-only in v1. An affix derivation (joyful for joy) counts for NEITHER venue.

mostlyright.core.schemas.earnings_fact.COUNTING_MODE_VALUES : tuple[str, …] = (‘boolean_ge1’, ‘threshold_ge_n’, ‘exact_n’, ‘range’)

Section titled “mostlyright.core.schemas.earnings_fact.COUNTING_MODE_VALUES : tuple[str, …] = (‘boolean_ge1’, ‘threshold_ge_n’, ‘exact_n’, ‘range’)”

Counting mode (RESEARCH-MARKETS §2.1(2)). mention_count is the integer primary tally; boolean is DERIVED as count >= 1 (boolean markets are boolean_ge1 with threshold_n == 1).

mostlyright.core.schemas.earnings_fact.DELIVERY_VALUES : tuple[str, …] = (‘live’, ‘hosted’)

Section titled “mostlyright.core.schemas.earnings_fact.DELIVERY_VALUES : tuple[str, …] = (‘live’, ‘hosted’)”

Delivery-channel lineage (D-27.2/D-27.6). INFORMATIONAL ONLY — not source identity. Mirrors SATELLITE_DELIVERY_VALUES.

class mostlyright.core.schemas.earnings_fact.EarningsFactSchema

Section titled “class mostlyright.core.schemas.earnings_fact.EarningsFactSchema”

Bases: Schema

schema.earnings_fact.v1 — one row per counted mention occurrence.

Carries the six RESEARCH-MARKETS §2 taxonomy dimensions (term match rule, counting mode, speaker scope, window scope, time, tie-break/resolution) plus the D-27.11 role_source provenance and the derived kalshi_counted flag, so the SAME fact rows resolve correctly under each venue’s wording.

mention_count is the integer PRIMARY tally; the boolean (“said ≥ 1”) is derived as mention_count >= 1 — storing only a bool cannot settle Polymarket “say X 5+ times” threshold brackets (RESEARCH-MARKETS §2.1(2)).

COLUMNS : ClassVar[list[ColumnSpec]] = [ColumnSpec(name=‘ticker’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=”), ColumnSpec(name=‘call_id’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=‘provider event id for the call’), ColumnSpec(name=‘term_canonical’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=‘canonical market term (§2.1(3) term.canonical)’), ColumnSpec(name=‘term_accepted_forms’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=‘JSON-encoded list of accepted surface forms (exploded slash + plural/possessive) — §2.1(3) term.accepted_forms’), ColumnSpec(name=‘term_match_rule’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘exact’, ‘plural_possessive_ok_no_tense’, ‘lemma’, ‘regex’), notes=‘§2.1(3) term.match_rule (plural/possessive OK, NOT tense)’), ColumnSpec(name=‘compound_type’, dtype=‘enum’, units=None, nullable=True, enum_values=(‘standalone’, ‘open’, ‘hyphenated’, ‘closed’, ‘affix_derivation’), notes=“D-30 §2.1(3) per-occurrence compound axis — SEPARATE from term_match_rule (do NOT overload MATCH_RULE_VALUES). standalone/open/hyphenated auto-count on both venues; closed is Polymarket candidate-only + Kalshi-No; affix_derivation counts for neither. Nullable: pre-fix rows omit it (default ‘standalone’)”), ColumnSpec(name=‘matched_surface_form’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=“the actually-spoken string (§2.1(3)) — kept verbatim so each occurrence is auditable against each venue’s stricter/looser rule”), ColumnSpec(name=‘mention_count’, dtype=‘int64’, units=None, nullable=False, enum_values=None, notes=‘PRIMARY integer tally (§2.1(2)); boolean is derived as >= 1 — storing only a bool cannot settle threshold brackets’), ColumnSpec(name=‘counting_mode’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘boolean_ge1’, ‘threshold_ge_n’, ‘exact_n’, ‘range’), notes=‘§2.1(2) counting_mode; boolean markets = boolean_ge1 (n=1)’), ColumnSpec(name=‘threshold_n’, dtype=‘int64’, units=None, nullable=True, enum_values=None, notes=‘§2.1(2) threshold_n (inclusive >= N); null for non-threshold modes’), ColumnSpec(name=‘speaker_role’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘company_executive’, ‘company_ir’, ‘operator_ir’, ‘operator_moderator’, ‘sell_side_analyst’, ‘other_external’, ‘unknown’), notes=‘§2.1(4) speaker_role — drives Kalshi/Polymarket scope divergence’), ColumnSpec(name=‘speaker_name’, dtype=‘string’, units=None, nullable=True, enum_values=None, notes=‘§2.1(4) speaker_name; nullable when unattributed’), ColumnSpec(name=‘role_source’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘transcript_structural’, ‘transcript_self_id’, ‘roster_match’, ‘diarization_advisory’), notes=‘D-27.11 provenance of the role attribution; only the three transcript-anchored values are Kalshi-countable (diarization is advisory only)’), ColumnSpec(name=‘segment’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘operator_intro’, ‘prepared_remarks’, ‘qa’, ‘closing’), notes=‘§2.1(5) call segment of the occurrence’), ColumnSpec(name=‘offset_seconds’, dtype=‘int64’, units=‘seconds’, nullable=True, enum_values=None, notes=‘§2.1(5) seconds into the call (audit trail); nullable’), ColumnSpec(name=‘confidence’, dtype=‘float64’, units=None, nullable=False, enum_values=None, notes=‘0..1 STT/attribution confidence (§2.1(6)) — flags borderline hits’), ColumnSpec(name=‘window_scope’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘full_call’, ‘prepared_remarks_only’, ‘qa_only’, ‘press_release_only’, ‘full_event_incl_release’), notes=‘§2.1(5) time-window scope a market resolves over’), ColumnSpec(name=‘resolution_status’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘pending’, ‘provisional’, ‘resolved_yes’, ‘resolved_no’, ‘no_qualifying_event’, ‘voided’, ‘disputed’), notes=“§2.1(6) resolution_status; ‘provisional’ = live early-signal delta (27-10, D-27.16) — NOT a settlement/backtest source”), ColumnSpec(name=‘kalshi_counted’, dtype=‘bool’, units=None, nullable=False, enum_values=None, notes=‘derived via validate_kalshi_counted_occurrence (D-27.11 fail-closed) — True iff role_source + speaker_role are anchorable’), ColumnSpec(name=‘is_final’, dtype=‘bool’, units=None, nullable=True, enum_values=None, notes=‘STT-segment finality ONLY (D-27.14/D-27.16); a provisional fact delta is is_final=True. NEVER gates settlement authority. Optional — batch rows omit/default True’), ColumnSpec(name=‘spoken_at’, dtype=‘timestamp_utc’, units=None, nullable=True, enum_values=None, notes=‘aired/event_time wallclock of the words (27-10, D-27.16); optional streaming marker — null on batch rows’), ColumnSpec(name=‘event_time’, dtype=‘timestamp_utc’, units=None, nullable=False, enum_values=None, notes=‘call air time (event-time)’), ColumnSpec(name=‘as_of_time’, dtype=‘timestamp_utc’, units=None, nullable=True, enum_values=None, notes=‘knowledge-time = transcript availability post-call (NOT event_date); for a live row stamped at STT-finalization/publish time and >= spoken_at (D-27.16); nullable’), ColumnSpec(name=‘source’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=“per-row source-identity overlay == ‘earnings_call’ (D-27.2)”), ColumnSpec(name=‘delivery’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘live’, ‘hosted’), notes=‘delivery-channel lineage {live,hosted}; NOT source identity (D-27.2)’)]

Section titled “COLUMNS : ClassVar[list[ColumnSpec]] = [ColumnSpec(name=‘ticker’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=”), ColumnSpec(name=‘call_id’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=‘provider event id for the call’), ColumnSpec(name=‘term_canonical’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=‘canonical market term (§2.1(3) term.canonical)’), ColumnSpec(name=‘term_accepted_forms’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=‘JSON-encoded list of accepted surface forms (exploded slash + plural/possessive) — §2.1(3) term.accepted_forms’), ColumnSpec(name=‘term_match_rule’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘exact’, ‘plural_possessive_ok_no_tense’, ‘lemma’, ‘regex’), notes=‘§2.1(3) term.match_rule (plural/possessive OK, NOT tense)’), ColumnSpec(name=‘compound_type’, dtype=‘enum’, units=None, nullable=True, enum_values=(‘standalone’, ‘open’, ‘hyphenated’, ‘closed’, ‘affix_derivation’), notes=“D-30 §2.1(3) per-occurrence compound axis — SEPARATE from term_match_rule (do NOT overload MATCH_RULE_VALUES). standalone/open/hyphenated auto-count on both venues; closed is Polymarket candidate-only + Kalshi-No; affix_derivation counts for neither. Nullable: pre-fix rows omit it (default ‘standalone’)”), ColumnSpec(name=‘matched_surface_form’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=“the actually-spoken string (§2.1(3)) — kept verbatim so each occurrence is auditable against each venue’s stricter/looser rule”), ColumnSpec(name=‘mention_count’, dtype=‘int64’, units=None, nullable=False, enum_values=None, notes=‘PRIMARY integer tally (§2.1(2)); boolean is derived as >= 1 — storing only a bool cannot settle threshold brackets’), ColumnSpec(name=‘counting_mode’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘boolean_ge1’, ‘threshold_ge_n’, ‘exact_n’, ‘range’), notes=‘§2.1(2) counting_mode; boolean markets = boolean_ge1 (n=1)’), ColumnSpec(name=‘threshold_n’, dtype=‘int64’, units=None, nullable=True, enum_values=None, notes=‘§2.1(2) threshold_n (inclusive >= N); null for non-threshold modes’), ColumnSpec(name=‘speaker_role’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘company_executive’, ‘company_ir’, ‘operator_ir’, ‘operator_moderator’, ‘sell_side_analyst’, ‘other_external’, ‘unknown’), notes=‘§2.1(4) speaker_role — drives Kalshi/Polymarket scope divergence’), ColumnSpec(name=‘speaker_name’, dtype=‘string’, units=None, nullable=True, enum_values=None, notes=‘§2.1(4) speaker_name; nullable when unattributed’), ColumnSpec(name=‘role_source’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘transcript_structural’, ‘transcript_self_id’, ‘roster_match’, ‘diarization_advisory’), notes=‘D-27.11 provenance of the role attribution; only the three transcript-anchored values are Kalshi-countable (diarization is advisory only)’), ColumnSpec(name=‘segment’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘operator_intro’, ‘prepared_remarks’, ‘qa’, ‘closing’), notes=‘§2.1(5) call segment of the occurrence’), ColumnSpec(name=‘offset_seconds’, dtype=‘int64’, units=‘seconds’, nullable=True, enum_values=None, notes=‘§2.1(5) seconds into the call (audit trail); nullable’), ColumnSpec(name=‘confidence’, dtype=‘float64’, units=None, nullable=False, enum_values=None, notes=‘0..1 STT/attribution confidence (§2.1(6)) — flags borderline hits’), ColumnSpec(name=‘window_scope’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘full_call’, ‘prepared_remarks_only’, ‘qa_only’, ‘press_release_only’, ‘full_event_incl_release’), notes=‘§2.1(5) time-window scope a market resolves over’), ColumnSpec(name=‘resolution_status’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘pending’, ‘provisional’, ‘resolved_yes’, ‘resolved_no’, ‘no_qualifying_event’, ‘voided’, ‘disputed’), notes=“§2.1(6) resolution_status; ‘provisional’ = live early-signal delta (27-10, D-27.16) — NOT a settlement/backtest source”), ColumnSpec(name=‘kalshi_counted’, dtype=‘bool’, units=None, nullable=False, enum_values=None, notes=‘derived via validate_kalshi_counted_occurrence (D-27.11 fail-closed) — True iff role_source + speaker_role are anchorable’), ColumnSpec(name=‘is_final’, dtype=‘bool’, units=None, nullable=True, enum_values=None, notes=‘STT-segment finality ONLY (D-27.14/D-27.16); a provisional fact delta is is_final=True. NEVER gates settlement authority. Optional — batch rows omit/default True’), ColumnSpec(name=‘spoken_at’, dtype=‘timestamp_utc’, units=None, nullable=True, enum_values=None, notes=‘aired/event_time wallclock of the words (27-10, D-27.16); optional streaming marker — null on batch rows’), ColumnSpec(name=‘event_time’, dtype=‘timestamp_utc’, units=None, nullable=False, enum_values=None, notes=‘call air time (event-time)’), ColumnSpec(name=‘as_of_time’, dtype=‘timestamp_utc’, units=None, nullable=True, enum_values=None, notes=‘knowledge-time = transcript availability post-call (NOT event_date); for a live row stamped at STT-finalization/publish time and >= spoken_at (D-27.16); nullable’), ColumnSpec(name=‘source’, dtype=‘string’, units=None, nullable=False, enum_values=None, notes=“per-row source-identity overlay == ‘earnings_call’ (D-27.2)”), ColumnSpec(name=‘delivery’, dtype=‘enum’, units=None, nullable=False, enum_values=(‘live’, ‘hosted’), notes=‘delivery-channel lineage {live,hosted}; NOT source identity (D-27.2)’)]”

mostlyright.core.schemas.earnings_fact.KALSHI_AUTOCOUNT_COMPOUND_TYPES : frozenset[str] = frozenset({‘hyphenated’, ‘open’, ‘standalone’})

Section titled “mostlyright.core.schemas.earnings_fact.KALSHI_AUTOCOUNT_COMPOUND_TYPES : frozenset[str] = frozenset({‘hyphenated’, ‘open’, ‘standalone’})”

Venue auto-count membership over COMPOUND_TYPE_VALUES (D-30). Both venues auto-count standalone/open/hyphenated in v1. closed is a candidate-only type — it never auto-counts; instead it surfaces for human review when it could flip a Polymarket outcome (fact_builder), and it is excluded from the Kalshi count entirely (Kalshi says No on a closed compound). affix_derivation is in NEITHER set (counts for no venue).

mostlyright.core.schemas.earnings_fact.KALSHI_COUNTABLE_ROLE_SOURCES : frozenset[str] = frozenset({‘roster_match’, ‘transcript_self_id’, ‘transcript_structural’})

Section titled “mostlyright.core.schemas.earnings_fact.KALSHI_COUNTABLE_ROLE_SOURCES : frozenset[str] = frozenset({‘roster_match’, ‘transcript_self_id’, ‘transcript_structural’})”

D-27.11 fail-closed membership sets. A Kalshi-counted occurrence MUST satisfy BOTH. Anything outside is excluded from the Kalshi count (retained for Polymarket’s any-speaker count).

mostlyright.core.schemas.earnings_fact.MATCH_RULE_VALUES : tuple[str, …] = (‘exact’, ‘plural_possessive_ok_no_tense’, ‘lemma’, ‘regex’)

Section titled “mostlyright.core.schemas.earnings_fact.MATCH_RULE_VALUES : tuple[str, …] = (‘exact’, ‘plural_possessive_ok_no_tense’, ‘lemma’, ‘regex’)”

Term match rule (RESEARCH-MARKETS §2.1(3)). Kalshi accepts exact/plural/ possessive but NOT verb-tense inflections.

mostlyright.core.schemas.earnings_fact.RESOLUTION_STATUS_VALUES : tuple[str, …] = (‘pending’, ‘provisional’, ‘resolved_yes’, ‘resolved_no’, ‘no_qualifying_event’, ‘voided’, ‘disputed’)

Section titled “mostlyright.core.schemas.earnings_fact.RESOLUTION_STATUS_VALUES : tuple[str, …] = (‘pending’, ‘provisional’, ‘resolved_yes’, ‘resolved_no’, ‘no_qualifying_event’, ‘voided’, ‘disputed’)”

Resolution status (RESEARCH-MARKETS §2.1(6)). "provisional" (27-10 Task 1) marks a LIVE early-signal fact delta — NOT a settlement/backtest source (D-27.16); authority is gated on this column + source, never on is_final.

mostlyright.core.schemas.earnings_fact.ROLE_SOURCE_VALUES : tuple[str, …] = (‘transcript_structural’, ‘transcript_self_id’, ‘roster_match’, ‘diarization_advisory’)

Section titled “mostlyright.core.schemas.earnings_fact.ROLE_SOURCE_VALUES : tuple[str, …] = (‘transcript_structural’, ‘transcript_self_id’, ‘roster_match’, ‘diarization_advisory’)”

D-27.11 role_source — the provenance of the speaker-role attribution. EXACTLY four values. Acoustic diarization is advisory only (it mis-clusters analyst Q&A as exec); only the three transcript-anchored sources are Kalshi-countable.

mostlyright.core.schemas.earnings_fact.SEGMENT_VALUES : tuple[str, …] = (‘operator_intro’, ‘prepared_remarks’, ‘qa’, ‘closing’)

Section titled “mostlyright.core.schemas.earnings_fact.SEGMENT_VALUES : tuple[str, …] = (‘operator_intro’, ‘prepared_remarks’, ‘qa’, ‘closing’)”

Call segment of the occurrence (RESEARCH-MARKETS §2.1(5)).

mostlyright.core.schemas.earnings_fact.SPEAKER_ROLE_VALUES : tuple[str, …] = (‘company_executive’, ‘company_ir’, ‘operator_ir’, ‘operator_moderator’, ‘sell_side_analyst’, ‘other_external’, ‘unknown’)

Section titled “mostlyright.core.schemas.earnings_fact.SPEAKER_ROLE_VALUES : tuple[str, …] = (‘company_executive’, ‘company_ir’, ‘operator_ir’, ‘operator_moderator’, ‘sell_side_analyst’, ‘other_external’, ‘unknown’)”

Speaker role of the occurrence (RESEARCH-MARKETS §2.1(4)). Drives the speaker-scope divergence between Kalshi (company-reps-incl-operator) and Polymarket (any speaker).

mostlyright.core.schemas.earnings_fact.WINDOW_SCOPE_VALUES : tuple[str, …] = (‘full_call’, ‘prepared_remarks_only’, ‘qa_only’, ‘press_release_only’, ‘full_event_incl_release’)

Section titled “mostlyright.core.schemas.earnings_fact.WINDOW_SCOPE_VALUES : tuple[str, …] = (‘full_call’, ‘prepared_remarks_only’, ‘qa_only’, ‘press_release_only’, ‘full_event_incl_release’)”

Time-window scope a market resolves over (RESEARCH-MARKETS §2.1(5)).

mostlyright.core.schemas.earnings_fact.validate_kalshi_counted_occurrence(role_source, speaker_role, , strict=False)

Section titled “mostlyright.core.schemas.earnings_fact.validate_kalshi_counted_occurrence(role_source, speaker_role, , strict=False)”

Fail-closed Kalshi-count rule (D-27.11).

Returns True iff the occurrence is anchorable for the Kalshi count: role_source ∈ KALSHI_COUNTABLE_ROLE_SOURCES AND speaker_role ∈ KALSHI_COUNTABLE_SPEAKER_ROLES. Returns False for EVERY other (role_source, speaker_role) pair — un-anchorable occurrences are excluded from the Kalshi count (recall loss accepted to avoid mis-settlement) but stay countable for Polymarket’s any-speaker rule.

This is the boolean a fact row’s kalshi_counted column is derived from. It is mode-agnostic: live (resolution_status="provisional") and batch occurrences are filtered identically. is_final (STT finality) NEVER gates this rule.

When strict=True and the occurrence is un-anchorable, raises KalshiCountRuleViolation (carrying the offending fields) — used at a write-site asserting an occurrence is Kalshi-counted.

  • Return type: bool
  • Parameters:
    • role_source (str)
    • speaker_role (str)
    • strict (bool)

mostlyright.core.schemas.earnings_fact.validate_kalshi_counted_rows(rows, , strict=True)

Section titled “mostlyright.core.schemas.earnings_fact.validate_kalshi_counted_rows(rows, , strict=True)”

Cross-field guard: a stored kalshi_counted=True MUST be derivable.

The schema validator checks kalshi_counted only as a bool dtype — it cannot see that a stored True is consistent with the row’s role_source / speaker_role. Settlement authority would then trust the boolean as-is, letting a row assert kalshi_counted=True while role_source="diarization_advisory" or the speaker is an analyst.

This post-validation hook re-derives the flag from validate_kalshi_counted_occurrence() and confirms it matches the stored value for EVERY row. A row claiming kalshi_counted=True whose (role_source, speaker_role) is un-anchorable raises KalshiCountRuleViolation (D-27.11 fail-closed) when strict; a row that under-counts (stored False while the pair is anchorable) is a contract bug too and raises EarningsFactCorruptError.

Returns the number of rows checked. Mirrors validate_satellite_station as a module-level hook (ColumnSpec has no cross-field rule).

mostlyright.core.schemas.earnings_fact.validate_ticker(ticker)

Section titled “mostlyright.core.schemas.earnings_fact.validate_ticker(ticker)”

Validate an earnings-fact row’s ticker identity.

Returns the ticker unchanged when valid (so call sites can use it inline). Raises EarningsFactCorruptError for a non-string or an empty string — mirrors validate_satellite_station. The acceptable ticker shape is intentionally permissive (issuers like BRK.B carry a dot); the contract enforced here is only non-empty-string.

  • Return type: str
  • Parameters: ticker (str)