mostlyright.weather.earnings.role_parser
mostlyright.weather.earnings.role_parser
Section titled “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 (
DeFucciforDiFucci,ElnickforZelnick) 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.
Functions
Section titled “Functions”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 |
Classes
Section titled “Classes”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_turns(transcript, roster=None)
Section titled “attribute_turns(transcript, roster=None)”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.
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:
confidence : float
Section titled “confidence : float”role_source : str
Section titled “role_source : str”speaker_name : str | None
Section titled “speaker_name : str | None”speaker_role : str
Section titled “speaker_role : str”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