Young's Mind
Podcast

TTS Formatting Rules — Podcast Lane (Compliance)

Owner: Eva (podcast lane). Set as card t_dc7f9b0e, 2026-10-21.Authoritative sources (ground truth for every rule below):

TTS Formatting Rules — Podcast Lane (Compliance)

Owner: Eva (podcast lane). Set as card t_dc7f9b0e, 2026-10-21.Authoritative sources (ground truth for every rule below):

  • /home/eva/.hermes/profiles/eva/scripts/podcast_script_template.py — fmt_ticker_to_name(), fmt_pause(), fmt_currency()
  • /home/eva/.hermes/profiles/eva/scripts/number_to_words.py — num_to_words(), fmt_percent_natural()

If any downstream prompt, cron, or script contradicts this file, this file wins — and the source functions above (not the prose) are what actually run.

Rule 1 — Full company names, never stock codes

Every ticker symbol in a script is replaced with its full company name before TTS renders. Handled by fmt_ticker_to_name().

Ticker-to-company map (as of 2026-10-21, verbatim from the source):

codespoken namecodespoken name
BHPBHP BillitonALLAristocrat Leisure
RIORio TintoREAREA Group
FMGFortescue MetalsGMGGoodman Group
CBACommonwealth BankTCLTransurban
NABNational Australia BankJHXJames Hardie
WBCWestpacASXthe ASX Group
ANZANZ BankNVDANvidia
MQGMacquarie GroupTSLATesla
CSLCSL LimitedAAPLApple
WESWesfarmersMSFTMicrosoft
WOWWoolworthsGOOGLAlphabet
TLSTelstraAMZNAmazon
STOSantosMETAMeta
BOEBoss EnergyZIPZip Co
DYLDeep YellowCXOCore Lithium
PDNPaladin EnergySUNSuncorp
S32South 32IAGInsurance Australia Group
BUBBubs AustraliaQBEQBE Insurance
MINMineral ResourcesPLSPilbara Minerals
IGOIGO LimitedLYCLynas Rare Earths
NVXNovonixSEMIthe semiconductor ETF
NDQthe Nasdaq 100 ETF

Extension rule: whenever a new name enters the watchlist, extend the map in fmt_ticker_to_name() — and the ticker list inside fmt_pause() (they are two separate hard-coded lists and must stay in sync; adding to only one means the name is spoken but with no pause). Same for the movers/all_names/single_names lists in the morning/afternoon/weekly generators when the name should appear in the movers section.

Rule 2 — Natural pauses before company names and percentages

TTS commas create spoken pauses. fmt_pause():

  • inserts a comma before any map ticker not already comma-prefixed: "…and then, BHP, it moved up…"
  • inserts a comma before percent / per cent: "…the ASX 200, it fell, one point eight percent…"
  • collapses doubled commas

The generators pass company-name phrases through fmt_pause() explicitly (e.g. fmt_pause('S&P 500'), fmt_pause('ASX 200'), fmt_pause('Hang Seng')).

Rule 3 — Decimal numbers as words

num_to_words() renders decimals in spoken word form, never digit strings:

valuespoken (whole part 0)spoken (with whole part)
0.50half"…and a half"
0.25a quarter"…and a quarter"
0.75three quarters"…and three quarters"
0.10a tenth"one point one"
0.20two tenths"one point two"
0.30three tenths"one point three"
0.40four tenths(pattern: N point d)
0.60–0.90six/nine tenths(pattern)
otherpoint one two three (digit-by-digit)e.g. "two point zero five"

Generic path: whole number then point then digits spoken one by one, trailing zeros stripped. Negatives are minus + magnitude. Large numbers render as thousand / million naturally.

Rule 4 — Zero is silent: say "flat"

num_to_words(0) returns "" — zero is never read aloud in a magnitude context. Every change-phrase path must therefore substitute a word, never an empty magnitude.

Source of truth (as of 2026-10-21, card t_9f0e8a99):

helperfilezero →non-zero →
fmt_percent_natural(n)number_to_words.pyflatup N percent / down N percent
describe_move(chg)podcast_script_template.pyfinished unchangedgained N percent / lost N percent (spoken words)
change_phrase(chg)podcast_script_template.pyflatup N.N percent / down N.N percent (digits)

describe_move() is used where the sentence carries no verb of its own (morning show: "The Nasdaq finished unchanged."); change_phrase() where a verb is already supplied (weekly: "the Nasdaq was flat"). Flatness tolerance is 0.05% — a move that rounds to 0.0% is treated as flat.

Hard rule: never emit an empty magnitude. Banned outputs — "The Nasdaq lost, percent." / "down 0.0 percent" / "up percent". fmt_percent_natural() also guards the sub-0.1% case (0.001% previously rendered as "up percent" because num_to_words(0) is silent).

Rule 5 — Symbols are spelled out

Per fmt_currency() and the script header rules (Young, 2026-10-01):

  • $ → X dollars ("five dollars", not "$5")
  • % → percent
  • & → and (e.g. "S&P 500" spoken "S and P five hundred")
  • No markdown symbols in TTS output (headers, #, * must never reach the voice)

Rule 6 — Conversational delivery

Short sentences. Natural rhythm. Conversational scaffolding allowed: "look", "here's the thing", "what's interesting is…". These per-script rules come from the template header (Young, 2026-10-01) and apply to all three shows (morning, afternoon wrap, weekly deep dive).

Known deviations / follow-ups (found 2026-10-21)

  1. Afternoon + weekly movers lines emit raw digit percentages (up {c:.1f} percent, e.g. "up 1.8 percent" instead of "up one point eight percent") — the movers strings in generate_afternoon_script() and generate_weekly_script() skip num_to_words()/fmt_percent_natural(). Violates Rule 3 in those sections only. Cathed lines elsewhere are compliant.
    • Update (card t_9f0e8a99): these movers lines now route through change_phrase(), which keeps the digit wording but eliminates the zero case ("flat" instead of "down 0.0 percent"). The Rule 3 deviation (digits vs spoken words) is still open — fixing it would change the voice of every afternoon/weekly episode, so it needs its own card.
  2. Map sync hazard: ticker list exists separately in fmt_ticker_to_name(), fmt_pause(), and three generators' name arrays — five places to update per new watchlist name (see Rule 1 extension rule).
  3. fmt_currency() only matches $<digits>, not $-prefixed words or ranges — scripts using "five dollars" spellings directly are already compliant.

Deviations were observed, not fixed, in this card — code changes belong to a podcast-lane build task, not the compliance snapshot.

Copyright © 2026