Skip to content

mostlyright.weather.earnings.fact_builder

Earnings fact-row builder + fail-closed Kalshi filter (Phase 27, 27-04).

Assembles schema.earnings_fact.v1-shaped rows from STT mention counts (27-03 mostlyright.weather.earnings.stt.count_mentions()) joined to role-parser turns (27-04 mostlyright.weather.earnings.role_parser.RoleParser), then applies the fail-closed Kalshi-count filter (D-27.11).

The two venues resolve the SAME fact rows through different filters (27-RESEARCH-MARKETS-RULES-STT §2):

  • Kalshi counts only occurrences spoken by a transcript-anchored company rep — kalshi_counted = validate_kalshi_counted_occurrence(role_source, speaker_role). An un-anchorable occurrence (analyst Q&A, or a diarization_advisory role_source) is EXCLUDED from the Kalshi count (recall loss accepted to avoid mis-settlement) but RETAINED in the rows.
  • Polymarket counts ANY speaker — every row contributes to the Polymarket tally regardless of kalshi_counted.

The builder is the ONLY place kalshi_counted is set, and it is ALWAYS derived from the canonical schema hook — never hand-set — so a settlement can never trust a stored boolean that the provenance does not support.

apply_kalshi_filter(rows)Set kalshi_counted on every row via the fail-closed rule (D-27.11).
build_fact_rows(stt_counts, turns, …[, …])Build schema.earnings_fact.v1 rows — one per (ticker, call_id, term, occurrence).
closed_candidate_count(rows)Sum of mention_count over closed-compound candidate rows (D-30).
kalshi_boolean_settles(rows)Boolean Kalshi settlement: `sum(mention_count
polymarket_count(rows)Polymarket any-speaker AUTO tally over auto-countable compound types.
polymarket_threshold_met(rows, threshold_n)Polymarket "say X N+ times" bracket: AUTO count >= threshold_n.
resolve_polymarket_status(rows, threshold_n)Fail-loud Polymarket resolution over the compound_type axis (D-30 decision 3).
resolve_status(rows, *, venue[, threshold_n])Return a resolution_status for the venue given the counted rows.

mostlyright.weather.earnings.fact_builder.apply_kalshi_filter(rows)

Section titled “mostlyright.weather.earnings.fact_builder.apply_kalshi_filter(rows)”

Set kalshi_counted on every row via the fail-closed rule (D-27.11).

kalshi_counted = validate_kalshi_counted_occurrence(role_source, speaker_role)True iff BOTH the role_source and speaker_role are Kalshi-anchorable. Un-anchorable occurrences (analyst Q&A, diarization_advisory role_source, unknown speaker) get False but are RETAINED in the returned rows for the Polymarket any-speaker count.

Returns NEW row dicts (does not mutate the inputs).

mostlyright.weather.earnings.fact_builder.build_fact_rows(stt_counts, turns, market_terms, , ticker, call_id, event_time=None)

Section titled “mostlyright.weather.earnings.fact_builder.build_fact_rows(stt_counts, turns, market_terms, , ticker, call_id, event_time=None)”

Build schema.earnings_fact.v1 rows — one per (ticker, call_id, term, occurrence).

stt_counts is a sequence of per-term occurrence records from the STT counter, each carrying at minimum term (the canonical market term), matched_surface_form (the actually-spoken string), and a turn_index linking the occurrence to the turns list (so its speaker role is known). Optional per-occurrence keys: offset_seconds, segment, confidence.

turns are role-parser Turn records (index-aligned with the transcript). market_terms is the per-term market spec carrying counting_mode, threshold_n, window_scope, term_match_rule, term_accepted_forms (JSON string), term_canonical — used to populate the venue-rule fields on each row.

Returns the rows with kalshi_counted ALREADY derived via apply_kalshi_filter() (fail-closed). Every occurrence is retained for the Polymarket any-speaker count; only kalshi_counted distinguishes the Kalshi-countable subset.

mostlyright.weather.earnings.fact_builder.closed_candidate_count(rows)

Section titled “mostlyright.weather.earnings.fact_builder.closed_candidate_count(rows)”

Sum of mention_count over closed-compound candidate rows (D-30).

These are NOT auto-counted for Polymarket, but they COULD flip an outcome under Polymarket’s “distinct component” wording — resolve_status() surfaces them for human review rather than settling resolved_no when they straddle the threshold.

mostlyright.weather.earnings.fact_builder.kalshi_boolean_settles(rows)

Section titled “mostlyright.weather.earnings.fact_builder.kalshi_boolean_settles(rows)”

Boolean Kalshi settlement: sum(mention_count | kalshi_counted) >= 1.

Only the Kalshi-counted (transcript-anchored company-rep) occurrences whose compound_type is auto-countable on Kalshi (standalone/open/hyphenatedKALSHI_AUTOCOUNT_COMPOUND_TYPES) contribute. A closed compound (wildfire for fire) is Kalshi-No — it NEVER flips a boolean Kalshi market — and an affix_derivation counts for neither venue. This is in ADDITION to the D-27.11 speaker-scope kalshi_counted gate (analyst / un-anchorable occurrences already excluded).

mostlyright.weather.earnings.fact_builder.polymarket_count(rows)

Section titled “mostlyright.weather.earnings.fact_builder.polymarket_count(rows)”

Polymarket any-speaker AUTO tally over auto-countable compound types.

Every occurrence counts regardless of kalshi_counted — Polymarket’s rule is speaker-blind (§2.1(4)) — BUT only compound_type in POLYMARKET_AUTOCOUNT_COMPOUND_TYPES (standalone/open/ hyphenated) auto-counts in v1. A closed compound is a candidate-only type (surfaced for human review by resolve_status(), NOT auto-counted); an affix_derivation counts for neither venue (D-30 decision 3/4).

mostlyright.weather.earnings.fact_builder.polymarket_threshold_met(rows, threshold_n)

Section titled “mostlyright.weather.earnings.fact_builder.polymarket_threshold_met(rows, threshold_n)”

Polymarket "say X N+ times" bracket: AUTO count >= threshold_n.

Uses the auto-countable polymarket_count() (closed candidates excluded). A closed-candidate straddle is handled by resolve_status() (fail-loud), not here.

mostlyright.weather.earnings.fact_builder.resolve_polymarket_status(rows, threshold_n)

Section titled “mostlyright.weather.earnings.fact_builder.resolve_polymarket_status(rows, threshold_n)”

Fail-loud Polymarket resolution over the compound_type axis (D-30 decision 3).

  • auto >= threshold -> "resolved_yes" (closed candidates irrelevant).
  • auto < threshold but auto + closed >= threshold -> the closed candidates COULD flip the outcome, so return the review status (_POLYMARKET_REVIEW_STATUS == "disputed") — NEVER a silent "resolved_no". A human adjudicates whether the closed compounds count.
  • auto + closed < threshold -> "resolved_no" (candidates cannot change the outcome, so no review is needed).

auto is polymarket_count() (standalone/open/hyphenated); affix_derivation rows count for neither venue and are ignored here.

mostlyright.weather.earnings.fact_builder.resolve_status(rows, , venue, threshold_n=None)

Section titled “mostlyright.weather.earnings.fact_builder.resolve_status(rows, , venue, threshold_n=None)”

Return a resolution_status for the venue given the counted rows.

venue is "kalshi" (boolean >=1 on Kalshi-counted, auto-countable compound-type rows) or "polymarket" (delegates to resolve_polymarket_status() — fail-loud on closed candidates). Returns "resolved_yes" / "resolved_no" per RESEARCH-MARKETS §2.1(6), plus "disputed" for a Polymarket closed-candidate straddle (D-30 decision 3). A no_qualifying_event (no rows at all) surfaces as "no_qualifying_event".

Kalshi resolution is unchanged except the compound-type restriction (a closed-only mention still resolves Kalshi-No, which is correct).