Launch polish PR-3: sponsor phone, folded tags, no stage directions (STORY-466/467/468) - #834
Merged
Merged
Conversation
AdScriptPromptBuilder cites "555-01" + two digits from an FNV-1a hash of the sponsor name folded to lower case (SPEC F199.1), so one sponsor's prompt is identical run to run and two sponsors rarely share an example. FNV-1a rather than string.GetHashCode, which is randomized per process. Story466 AC1-AC2 green (acme -> 555-0115, zenith -> 555-0129); AC3-AC6 stay pending for T550. Deployed proof: a Wire Diner write job's recorded system prompt reads "for example 555-0192".
AdScriptWriter runs a phone hygiene pass after the LLM reply: a phone-shaped run becomes the sponsor's own number when one is on file (STORY-466 AC3), a run whose digits already equal it is left alone in any format (AC4), and with no phone on file the clause carrying the placeholder is dropped, orphan punctuation stripped, and an emptied line removed (AC5, SPEC F199.2). PhoneShapeCheck tightens per SPEC F199.3: with a sponsor phone on file only that number passes; a differing run refuses even when it contains 555 (AC6). Story417's filler numbers move to the sponsor's own phone for that reason, and the Host completions router drops its placeholder clause so Story423's byte-for-byte pin holds for phone-less sponsors. The scan advances past each replacement instead of restarting at zero; the earlier restart re-matched the inserted tail forever for paren-formatted phones and starved the Ads test threadpool.
The parser no longer refuses a script whose model-invented tag (NARRATOR,
HOST, ...) is not ANNOUNCER/VOICE1/VOICE2: the line folds onto ANNOUNCER
and the fold is recorded as a parse note (`unknown-tag:{TAG}`). Distinct
and ANNOUNCER checks run over the folded lines (SPEC F200.1/F200.2), so
notes are never a violation. GET /api/ads/{id} exposes `parseNotes`;
render/preview log one INFO line per note (F200.3).
Story390's four-tag refusal pinned a state F200 defines away; it now pins
the fold. Un-skips the six Story467 facts.
AdScriptWriter.ApplyLineAwareHygiene strips (…), […] and *…* anywhere on a line before the general copy hygiene runs, so "*long pause*" is removed whole instead of being read aloud; whitespace and a stranded space before punctuation are tidied only when a shape was actually removed. Lines left empty are dropped before the lead-voice election so they never vote (SPEC F201.1). Each shape must carry a letter so a sponsor's "(406)" area code survives for the phone rewrite. AdScriptValidator gains rule `stage_direction` (F201.2): post-hygiene residue refuses naming the line and a bounded echo. The echo bound moves to a shared AdScriptEcho helper. CheckPhoneShape's reason is now honest for both branches (F199.3). Un-skips Story468.
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-3 of the launch-polish epic: STORY-466 (the sponsor's phone replaces the placeholder), STORY-467 (unknown speaker tags fold into the announcer), STORY-468 (stage directions never reach the voice). Built on PR-2 (#833, merged); four commits ahead of
main.Fixes #701, fixes #742, fixes #706.
AdScriptPromptBuilderderives the example number from the sponsor id, so no two sponsors see the same placeholder and the small model's habit of copying it cannot put one number on every spot.AdScriptWriter.ApplyPhoneHygienerewrites any phone-shaped run to the sponsor's own number in the sponsor's own format, in any NANP shape including(812) 346-0199; without a phone on file the phone clause is dropped whole and the line re-capitalised.PhoneShapeChecktightens (SPEC F199.3): with a phone on file only that exact digit string passes, so a stray 555 number still refuses.AdScriptParserfolds any tag outside ANNOUNCER/VOICE1/VOICE2 onto ANNOUNCER and recordsunknown-tag:{TAG}; the distinct-tag count runs after the fold, so a misspelt tag never trips "got 4".GET /api/ads/{id}carriesparseNotes; render and preview log one line per note.ApplyLineAwareHygienestrips(…),[…]and*…*anywhere on a line before the general copy hygiene, tidies only when a shape was removed, and drops emptied lines before the lead-voice election.AdScriptValidatorgains rulestage_directionon post-hygiene residue. Each shape must carry a letter so a sponsor's(406)area code survives.🔬 Wire evidence (T553, dev station on the branch image)
Three spots regenerated through
POST /api/ads+/writeon the deployed api, read back throughGET /api/ads/{id}:(812) 346-0199on file(812) 346-0199once, no other phone-shaped run, no stage directionparseNotes=["unknown-tag:NARRATOR"]; preview loggedAd spot 39 parse note unknown-tag:NARRATORBriefs for 37 and 38 asked the model for delivery notes in parentheses; both stored scripts have none.
STORY-468 through the same api (T552 smoke): the model's raw reply for a Harbor Bakery spot carried five stage directions (
(enthusiastically),(warmly, pause), …) and a stray555-0193; the stored script has none of either. An owner-typed script with(beat)on line 2 returns 400stage_direction"script line 2 still carries a stage direction ("(beat)")".STORY-466 through the same api (T550 smoke): with the phone on file the script carried exactly
(812) 346-0199; without it the model's "Call us now at 555-0193 or visit us online…" became "Or visit us online…".Full solution (
dotnet test GenWave.sln --filter "Category!=Integration", Host withMaxParallelThreads=3): 0 failed across all 9 projects (Ads 231, Tts 930 / 2 skipped, Host 3083 / 47 skipped). admin-ui untouched.AdScriptWriter.ApplyPhoneHygieneduplicatesGenWave.Core.PhoneShape(PR-2); dedupe once both are onmain.AdScriptParsernow depends onAdCastPickerfor the VOICE1/VOICE2 tag names (format stage reaching into render stage); the tags belong besideAnnouncerTag. Candidate for the Burn the optional-seam baseline in GenWave.Orchestration down to zero (SPEC F193.2) #827 seam burn-down.ILoggerand the panel half has no PLAN task. SPEC prose also writesstage-directionwhere the rule id isstage_direction. /design amendment owed.555-0193 | site | hours) leaves| site | hoursjoined to the previous line after the clause drop. Cosmetic; seen once.