Skip to content

mostlyright.weather.earnings.role_parser

Transcript-anchored role-attribution parser (Phase 27, 27-04).

THE single highest settlement-risk component in the phase. A mis-attributed analyst-as-executive turn flips a Kalshi NHIGH/NLOW-style earnings-mention settlement. Acoustic diarization FAILED the analyst/exec split (~51% DER, 99% of analyst frames landed in an exec-majority cluster — 27-RESEARCH-QA-DIARIZATION §2) and is DROPPED from the critical path (D-27.11). Roles come from TEXT only.

The transcript hands us roles for free:

  • the operator announces every analyst by name+firm ( “Our first question comes from John DiFucci from Guggenheim”) — role_source="transcript_structural";
  • analysts self-identify on taking the mic — role_source="transcript_self_id";
  • executives are the named answerers from the prepared-remarks roster — role_source="transcript_structural" / transcript_self_id;
  • a mangled surname (DeFucci for DiFucci, Elnick for Zelnick) is resolved by fuzzy-matching against the published participant roster, GATED on the cleanly-transcribed firm token (firms survive STT exact — §1) — role_source="roster_match".

A turn we cannot anchor to any of the three transcript-anchored sources gets role_source="diarization_advisory" (or "unknown") — NEVER a Kalshi-countable source. That drives the fail-closed exclusion downstream (mostlyright.core.schemas.earnings_fact.validate_kalshi_counted_occurrence()).

Fuzzy matching uses difflib.get_close_matches() (stdlib) — deliberately NOT a third-party fuzzy lib (no new-dependency legitimacy gate; the surname-repair job is tiny and difflib’s ratio is sufficient here). The match is gated on the firm token so a close surname in the WRONG firm cannot be adopted.

fuzzy_match_surname(spoken, roster, …[, …])Repair a mangled analyst surname against the published roster.
parse_operator_announcements(text)Extract operator analyst hand-off announcements from text.
parse_self_identifications(text)Extract analyst self-identifications (“This is from ”).
RoleParser([roster])Attribute transcript turns to speaker roles from TEXT cues only (D-27.11).
RosterEntry(name, firm[, role])A published-participant roster row: canonical name, firm, and role.
Turn(speaker_name, speaker_role, role_source)A single attributed transcript turn.

class mostlyright.weather.earnings.role_parser.RoleParser(roster=None)

Section titled “class mostlyright.weather.earnings.role_parser.RoleParser(roster=None)”

Bases: object

Attribute transcript turns to speaker roles from TEXT cues only (D-27.11).

Construct with the published participant roster ((name, firm) pairs or RosterEntry rows). attribute_turns walks a segmented transcript and assigns each turn a speaker_role + role_source, resolving mangled analyst surnames via fuzzy_match_surname() gated on the firm token. A turn with no structural cue and no roster hit is left role_source="diarization_advisory" — NEVER a Kalshi-countable source.

Attribute every turn in transcript to a role from TEXT cues.

transcript is either the raw transcript text (parsed for operator announcements + self-IDs + label lines) OR a list of pre-segmented turn dicts ({"speaker_name": ..., "label": ..., "text": ...}). roster overrides the instance roster for this call.

Each returned Turn carries speaker_role + role_source. Un-anchorable turns get role_source="diarization_advisory".

class mostlyright.weather.earnings.role_parser.RosterEntry(name, firm, role=‘unknown’)

Section titled “class mostlyright.weather.earnings.role_parser.RosterEntry(name, firm, role=‘unknown’)”

Bases: object

A published-participant roster row: canonical name, firm, and role.

role is one of the SPEAKER_ROLE_VALUES (e.g. sell_side_analyst for an analyst, company_executive for a named exec). The firm token is the fuzzy-match GATE — a surname is only repaired against entries sharing the cleanly-transcribed firm.

  • Parameters:

class mostlyright.weather.earnings.role_parser.Turn(speaker_name, speaker_role, role_source, firm=None, text=”, confidence=1.0)

Section titled “class mostlyright.weather.earnings.role_parser.Turn(speaker_name, speaker_role, role_source, firm=None, text=”, confidence=1.0)”

Bases: object

A single attributed transcript turn.

speaker_role is a SPEAKER_ROLE_VALUES member; role_source is a ROLE_SOURCE_VALUES member. An un-anchorable turn carries role_source="diarization_advisory" (or "unknown") — never a Kalshi-countable source, so the downstream fail-closed filter excludes it from the Kalshi count.

  • Parameters:
    • speaker_name (str | None)
    • speaker_role (str)
    • role_source (str)
    • firm (str | None)
    • text (str)
    • confidence (float)

mostlyright.weather.earnings.role_parser.fuzzy_match_surname(spoken, roster, firm_token, , cutoff=0.72)

Section titled “mostlyright.weather.earnings.role_parser.fuzzy_match_surname(spoken, roster, firm_token, , cutoff=0.72)”

Repair a mangled analyst surname against the published roster.

spoken is the (possibly mis-transcribed) surname as heard — "DeFucci" for "DiFucci", "Elnick" for "Zelnick". roster is the published participant list as (name, firm) pairs (or RosterEntry rows). firm_token is the cleanly-transcribed firm — the fuzzy match is GATED on it: only roster entries whose firm matches firm_token (exact, normalized) are candidates, so a close surname in the WRONG firm can never be adopted (the cross-firm mis-attribution hazard).

Returns the roster’s CANONICAL surname on a confident match, else None. Uses difflib.get_close_matches() (stdlib): the surname-repair job is tiny, and difflib’s SequenceMatcher ratio comfortably resolves the 1-char / phonetic drifts observed (DeFucci→DiFucci ratio ~0.86, Elnick→Zelnick ~0.77). No third-party fuzzy dependency is pulled (no new-dep legitimacy gate).

mostlyright.weather.earnings.role_parser.parse_operator_announcements(text)

Section titled “mostlyright.weather.earnings.role_parser.parse_operator_announcements(text)”

Extract operator analyst hand-off announcements from text.

Returns one dict per announcement:

{"name": "John DiFucci", "firm": "Guggenheim",
"role_source": "transcript_structural"}

The operator names every analyst by name+firm; this is the most reliable transcript-anchored role cue (§1). The extracted analyst is a sell_side_analyst — the caller assigns the role. Firm is captured verbatim so it can gate a downstream roster fuzzy-match on a mangled surname.

mostlyright.weather.earnings.role_parser.parse_self_identifications(text)

Section titled “mostlyright.weather.earnings.role_parser.parse_self_identifications(text)”

Extract analyst self-identifications (“This is from ”).