feat(tts): phone numbers as digits + the flatten keeps the pause (gh-#700, gh-#703) - #833
Merged
Merged
Conversation
GenWave.Core.PhoneShape holds the one NANP phone-shape regex (SPEC F197.1);
Ads.PhoneShapeCheck matches through it and keeps no regex of its own. The
pattern is byte-identical to the one Ads carried, so the 555 rule (F160.3)
and the owner-sponsor phone skip (F172.5) behave exactly as before.
STORY-464 AC1 (four shapes) is pinned in Core.Tests; AC2 (no \d{3} regex
literal anywhere in Ads) is a fitness law in Architecture.Tests — a
compiled-attribute scan plus a source-text scan — because referencing
GenWave.Ads from Core.Tests makes the GenWave.Loudness namespace shadow the
unqualified Loudness domain type there (CS0118).
Smoke on the deployed api: POST /api/ads refuses "812-0142" and
"812.301.0199" with the phone_shape violation and accepts "(812) 555-0199".
SpeechText.FlattenSegment runs a spoken-digit pass on the raw segment
before every prose pass: each GenWave.Core.PhoneShape match becomes its
digits spoken one by one, groups joined by a comma (SPEC F197.2). Years,
times, and counts never match the shape, so they pass through untouched
(F197.3). The prose passes moved into FlattenProse and run on the text
between matches, so the emitted comma survives ClauseMarkRx.
Story464 AC3-AC6 and AC8 green; AC7 ("7:30" unchanged) asserts the story
and stays skipped pending T546, which narrows the colon strip (F198).
MergePolicyVersion is untouched; T546 bumps it once for the branch.
LooseMarkRx keeps the comma the writer put there. A loose colon, semicolon, spaced dash, or ellipsis folds to exactly one comma and runs collapse to one (SPEC F198.1); in-word hyphens and an intra-digit colon such as 7:30 are untouched. The comma-run collapse runs once over the assembled segment so the space before a spoken phone number or a markup token survives (F197.2 + F198.1). TtsSegmentSource.MergePolicyVersion is now f97.4+gh541+gh703 (F198.2), so every evergreen clip re-renders once after upgrade. Story465 AC1-AC7 green plus three fragment-boundary pins; Story464 AC7 un-skipped. Specs that pinned a stripped comma or loose mark re-pinned to the F198.1 output: Gh541, Gh491, Gh161, Story005, Story456, Story185, Story186.
CrosstalkTimeline maps a transition's single seeded draw into [0.20, 0.35] s when the two lines share a speaker, and keeps the 0.2-0.8 s range between different speakers (SPEC F198.3). One draw per transition, in order, so flagging a transition never reshuffles later jitter. Both assembler paths thread the per-transition flags through MixAsync: crosstalk compares the speaker enum, cast lines compare the tag ordinally. Story465 AC8-AC9 green; Story327 call sites carry the new parameter with their alternating scripts. Deployed proof: the same four-line spot rendered 0.799 s shorter after the change, matching the timeline's predicted delta to the millisecond.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎯 What
PR-2 of the launch-polish epic: STORY-464 (phone numbers are spoken as digits) + STORY-465 (the flatten keeps the pause). Built on PR-1 (#830, merged); four commits ahead of
main.Fixes #700, fixes #703.
GenWave.Core.PhoneShapeowns the four NANP shapes;PhoneShapeCheckin Ads references it and keeps no\d{3}regex of its own (fitness law in Architecture.Tests).SpeechText.FlattenSegmentsplits each segment around phone matches and speaks them digit by digit, groups joined with ", ". Years, times (7:30) and plain counts are untouched.ac/dcstay (F68 law).MergePolicyVersionmoves tof97.4+gh541+gh703(F198.2) so every cached evergreen line re-renders once.CrosstalkTimelineclamps a same-speaker transition into [0.20, 0.35] s from the same seeded draw; cross-speaker transitions keep [0.2, 0.8] s. Both assembler paths (crosstalk speaker enum, cast tag) thread the flags throughMixAsync.🔬 Wire evidence (T548, dev station on the branch image)
STORY-464 AC3/AC4/AC6–AC8 and STORY-465 AC1–AC6 through
POST /api/tts/normalize-previewon the deployed api (the oneSpeechText.Normalizechokepoint every voice uses):STORY-464 AC5 (every voice) — the same three-line spot rendered through
POST /api/ads+/previewon the t545 image and again on the t547 image; the text Kokoro logged as its input:STORY-465 AC8/AC9 — a four-line spot (ANNOUNCER ×3, VOICE1) previewed with the same seed before and after T547: gaps 0.729 / 0.737 / 0.451 s → 0.332 / 0.334 / 0.451 s; the preview wav shrank 0.799 s against a predicted 0.799 s.
F198.2 re-render — after each api restart on the new
MergePolicyVersionthe booth log shows the evergreen lines re-rendering (fivepatter-airedrows in the first 40 s, AIR rows resuming within twenty minutes: first LeadIn at 20:30:57Z after the 20:12Z restart, 30 renders ahead of it).STORY-465 AC10 (manual) — clip pairs for Dean's ear at
~/pace-ear-check/2026-09-22-flatten-pause/index.html(pause pair t545→t547, gap pair t546→t547, same script each).Full solution (
dotnet test GenWave.sln --filter "Category!=Integration", Host withMaxParallelThreads=3): 0 failed across all 9 projects (Host 3082 passed / 48 skipped, Tts 907 / 16 skipped). admin-ui untouched.AssembleCastAsyncend to end with two consecutive same-tag lines; AC8 pinsCrosstalkTimelineand the wire proof covers the assembler. A one-fact wiring spec would close that gap.well,.) is deliberately not normalised; only a true trailing,collapses.GenWave.Loudnessnamespace shadow theLoudnessrecord there (CS0118). Noted in both spec headers.