test: preserve dialogue-only recast merges

This commit is contained in:
mARTin-B78 2026-09-04 15:31:45 +02:00
parent b6f4a9518b
commit 29f5243f6b
6 changed files with 41 additions and 10 deletions

View File

@ -8,6 +8,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
## [Unreleased] ## [Unreleased]
### Changed ### Changed
- **Dialogue-only merge regression coverage.** Recast/verify normalization now has explicit browser coverage proving that
only adjacent known-speaker dialogue merges; narration and unknown-speaker lines remain separate.
- **Visual identity regression coverage.** Portrait and concept-sheet prompt tests now verify shared identity anchors; - **Visual identity regression coverage.** Portrait and concept-sheet prompt tests now verify shared identity anchors;
completed pairs centralize prompt-version, identity-fingerprint, and asset-digest metadata. completed pairs centralize prompt-version, identity-fingerprint, and asset-digest metadata.
- **Frontend namespace migration completed.** The remaining utility, library, cast-compatibility, i18n, and settings/log - **Frontend namespace migration completed.** The remaining utility, library, cast-compatibility, i18n, and settings/log

View File

@ -268,7 +268,7 @@ codebase; benefit is relative user/reliability impact.
- [x] **P1 / M — High benefit — Identity-anchor regression tests.** Add automated coverage proving avatar and concept-sheet - [x] **P1 / M — High benefit — Identity-anchor regression tests.** Add automated coverage proving avatar and concept-sheet
requests share canonical identity anchors and persist the prompt version used for each generated asset. requests share canonical identity anchors and persist the prompt version used for each generated asset.
- [ ] **P1 / S–M — Medium benefit — Preserve dialogue-only merging.** Keep the dialogue-only merge for recast/verify - [x] **P1 / S–M — Medium benefit — Preserve dialogue-only merging.** Keep the dialogue-only merge for recast/verify
outputs where adjacent pairs still occur, with regression coverage for the non-dialogue boundaries. outputs where adjacent pairs still occur, with regression coverage for the non-dialogue boundaries.
- [ ] **P1 / L–XL — Very high benefit — Rework segmentation from Run 3.** Inspect the earlier dialogue split/fusion - [ ] **P1 / L–XL — Very high benefit — Rework segmentation from Run 3.** Inspect the earlier dialogue split/fusion
stages and correct the dominant unmatched-segment class using the measured Run 3 artifacts. stages and correct the dominant unmatched-segment class using the measured Run 3 artifacts.

View File

@ -1 +1 @@
1.24.99 1.25.0

View File

@ -88,6 +88,7 @@ Done incrementally as global-scope ordered files (no ES-module change needed)
- ✅ All remaining application frontend modules are either privately scoped or intentionally stateless bootstrap/cache IIFEs; - ✅ All remaining application frontend modules are either privately scoped or intentionally stateless bootstrap/cache IIFEs;
legacy globals are explicit compatibility exports and new integrations use `window.TTVC` namespaces. legacy globals are explicit compatibility exports and new integrations use `window.TTVC` namespaces.
- ✅ Visual pair generation centralizes prompt-version, identity-fingerprint, and asset-digest metadata with regression coverage for shared anchors. - ✅ Visual pair generation centralizes prompt-version, identity-fingerprint, and asset-digest metadata with regression coverage for shared anchors.
- ✅ Recast/verify dialogue merging has regression coverage for known-speaker-only behavior and non-dialogue boundaries.
- ✅ `static/css/resource-diagnostics.css` — isolated Performance/resource diagnostic panel styles. - ✅ `static/css/resource-diagnostics.css` — isolated Performance/resource diagnostic panel styles.
- ✅ `static/css/background-jobs.css` — isolated background-job history and retry-panel styles. - ✅ `static/css/background-jobs.css` — isolated background-job history and retry-panel styles.
- ✅ `static/css/voice-design.css` — isolated Voice Design form, samples, and preview-match styles. - ✅ `static/css/voice-design.css` — isolated Voice Design form, samples, and preview-match styles.

View File

@ -10,7 +10,7 @@
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#2563EB"> <meta name="theme-color" content="#2563EB">
<meta name="app-version" content="1.24.99"> <meta name="app-version" content="1.25.0">
<link rel="manifest" href="/manifest.webmanifest"> <link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="/static/icon.svg" type="image/svg+xml"> <link rel="icon" href="/static/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/static/icon.svg"> <link rel="apple-touch-icon" href="/static/icon.svg">
@ -27,12 +27,12 @@
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── --> <!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css"> <link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
<link rel="stylesheet" href="/static/style.css?v=1.24.99"> <link rel="stylesheet" href="/static/style.css?v=1.25.0">
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.24.99"> <link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.25.0">
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.24.99"> <link rel="stylesheet" href="/static/css/background-jobs.css?v=1.25.0">
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.24.99"> <link rel="stylesheet" href="/static/css/voice-design.css?v=1.25.0">
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.24.99"> <link rel="stylesheet" href="/static/css/rehearser.css?v=1.25.0">
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.24.99"> <link rel="stylesheet" href="/static/css/audiobook.css?v=1.25.0">
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── --> <!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
@ -397,7 +397,7 @@ window.toggleNavTree = function(treeId, chevronId) {
</script> </script>
<!-- loader.js: fetches sections → loads JS modules → removes skeleton --> <!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
<script src="/static/loader.js?v=1.24.99"></script> <script src="/static/loader.js?v=1.25.0"></script>
</body> </body>
</html> </html>

View File

@ -41,6 +41,34 @@ test('Audiobook lazy modules publish their namespace APIs after loading', async
}))).toEqual({ castView: 'function', stream: 'function', startCast: 'function', pipelineCast: 'function', core: 'function', preview: 'function', exportAudio: 'function' }); }))).toEqual({ castView: 'function', stream: 'function', startCast: 'function', pipelineCast: 'function', core: 'function', preview: 'function', exportAudio: 'function' });
}); });
test('Audiobook recast merge preserves narration and merges only known dialogue pairs', async ({ page }) => {
await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 });
await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 });
await page.evaluate(() => window.navTo('s-reader'));
await page.waitForFunction(() => typeof window.TTVC?.audiobook?.cast?.mergeAdjacentDialogue === 'function', null, { timeout: 15_000 });
await expect.poll(() => page.evaluate(() => {
const merge = window.TTVC.audiobook.cast.mergeAdjacentDialogue;
return merge([
{ type: 'narration', speaker: 'Narrator', text: 'A.' },
{ type: 'narration', speaker: 'Narrator', text: 'B.' },
{ type: 'dialogue', speaker: 'Mira', text: 'C.' },
{ type: 'dialogue', speaker: 'Mira', text: 'D.' },
{ type: 'dialogue', speaker: 'Unknown', text: 'E.' },
{ type: 'dialogue', speaker: 'Unknown', text: 'F.' },
]);
})).toEqual({
segments: [
{ type: 'narration', speaker: 'Narrator', text: 'A.' },
{ type: 'narration', speaker: 'Narrator', text: 'B.' },
{ type: 'dialogue', speaker: 'Mira', text: 'C.\n\nD.' },
{ type: 'dialogue', speaker: 'Unknown', text: 'E.' },
{ type: 'dialogue', speaker: 'Unknown', text: 'F.' },
],
merged: 1,
});
});
test('Rehearser playback module publishes transport APIs after loading', async ({ page }) => { test('Rehearser playback module publishes transport APIs after loading', async ({ page }) => {
await page.goto('/', { waitUntil: 'domcontentloaded' }); await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 }); await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 });