diff --git a/AGENTS.md b/AGENTS.md
index cd1e980..334aa68 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -133,6 +133,8 @@ If a change does not cleanly fit either exempt or substantive buckets, ask the u
Use the `impeccable` skill to structure and style the document as clean, readable HTML. For this repository, `impeccable` is the styling and layout authority for turn documents when available. Do not apply global non-repo computer-task house styling to repository turn documents.
+Future turn documents must start from `docs/turns/template.html`. The template is the canonical appearance baseline for this repository: dark polished layout, lavender and pink accent colors, compact header metadata, clear section rhythm, and contained diff shells modeled after `/Users/kell/dev/islandflow/docs/turns/2026-05-20-fix-alert-flow-packet-history.html`.
+
If `impeccable` is unavailable or blocked by an actual tool/file error, still create a well-structured standalone HTML file.
Each turn document must include these sections:
@@ -157,7 +159,7 @@ For a minor update to a previous substantive change, add this section to the exi
### Rendered Diff Documentation
-When turn documentation needs rendered code diffs, use `@pierre/diffs` through its ESM server-side renderer.
+When turn documentation needs rendered code diffs, use Clean SSR with `@pierre/diffs` through its ESM server-side renderer. The final HTML must be readable as a static file and must not depend on client-side package loading.
Use `@pierre/diffs/ssr` with Node ESM imports. Do not test, load, or diagnose this package with CommonJS `require()`, because `@pierre/diffs` is ESM and `require('@pierre/diffs/ssr')` can falsely look like an export or package failure.
@@ -188,7 +190,16 @@ NODE
Do not run `npx @pierre/diffs`; the package is a rendering library and does not expose a CLI executable.
-Only use a clearly labeled plain diff or code-block fallback when the ESM import-and-render pattern above fails because of a real tool, install, or runtime error. Document the failure briefly in the turn document.
+Diff output must follow these readability rules:
+
+- Use curated, relevant snippets rather than dumping an entire commit or full-file diff when a focused snippet explains the change.
+- Render one file diff per `.diff-shell`, with a clear `.diff-title` naming the file and purpose.
+- Insert generated SSR HTML only inside the matching `.diff-view` element from `docs/turns/template.html`.
+- Never paste generated SSR output as a freestanding section body or outside the diff shell.
+- Keep normal prose sections around the generated markup so the source and rendered page remain navigable.
+- Mark a shell with `class="diff-shell rendered"` when SSR output is present so the fallback is hidden.
+
+Only use a clearly labeled plain diff or code-block fallback in the template's `
` block when the ESM import-and-render pattern above fails because of a real tool, install, or runtime error. Document the failure briefly in the turn document.
## Plan Mode Documentation
diff --git a/CLAUDE.md b/CLAUDE.md
index 633878c..69c2703 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -112,7 +112,7 @@ Use this decision order before creating a turn document:
The minor/trivial exemptions override the general mandatory turn-document rule.
-For diff content in turn documentation (including "Code diffs" and "Relevant Diff Snippets"), use `@pierre/diffs` output by default. Do not run `npx @pierre/diffs`; the package is a rendering library and does not expose a CLI executable. Generate rendered diff HTML with `@pierre/diffs/ssr`, usually `preloadPatchDiff`, and insert that rendered output into the turn document. `preloadPatchDiff` expects exactly one file diff per call, so split multi-file diffs into one patch per file and concatenate the rendered HTML. If `@pierre/diffs/ssr` is unavailable because of a real tool or blocking error, use a clearly labeled plain diff/code block fallback and note why.
+For diff content in turn documentation (including "Code diffs" and "Relevant Diff Snippets"), use Clean SSR with `@pierre/diffs` output by default. Do not run `npx @pierre/diffs`; the package is a rendering library and does not expose a CLI executable. Generate rendered diff HTML with `@pierre/diffs/ssr`, usually `preloadPatchDiff`, and insert that rendered output only inside a `.diff-view` element within a `.diff-shell` from `docs/turns/template.html`. `preloadPatchDiff` expects exactly one file diff per call, so split multi-file diffs into one patch per file and render one shell per file. Use curated, relevant snippets rather than dumping an entire commit or full-file diff when a focused snippet explains the change. If `@pierre/diffs/ssr` is unavailable because of a real tool or blocking error, use the template's clearly labeled plain fallback diff block and note why.
### No turn document for minor/trivial checklist matches
@@ -132,7 +132,7 @@ If a change does not cleanly fit either exempt or substantive buckets, ask the u
**"New Changes as of {time and date at which the change was made}"**
- **Summary of changes**
- **Why this change was made**
-- **Code diffs** (use rendered `@pierre/diffs/ssr` output by default; do not use `npx @pierre/diffs`; if unavailable, include a clearly labeled plain diff/code block and note why)
+- **Code diffs** (use Clean SSR `@pierre/diffs/ssr` output inside the template's `.diff-view` by default; do not use `npx @pierre/diffs`; if unavailable, include a clearly labeled plain fallback diff block and note why)
- **Related issues or PRs**
Additionally, add a note to each section explaining why the changes were made.
@@ -163,6 +163,8 @@ Use the `impeccable` skill to structure and style the document as clean, readabl
For this repository, `impeccable` is the styling and layout authority for turn documents when available. Do not apply global non-repo computer-task house styling to repository turn documents.
+Future turn documents must start from `docs/turns/template.html`. The template is the canonical appearance baseline for this repository: dark polished layout, lavender and pink accent colors, compact header metadata, clear section rhythm, and contained diff shells modeled after `/Users/kell/dev/islandflow/docs/turns/2026-05-20-fix-alert-flow-packet-history.html`.
+
If the `impeccable` skill is unavailable or blocked by an actual tool/file error, still create a well-structured standalone HTML file with:
- A concise summary at the top
@@ -181,7 +183,7 @@ Each turn document must include these sections:
2. **Changes Made**
3. **Context**
4. **Important Implementation Details**
-5. **Relevant Diff Snippets** (render with `@pierre/diffs/ssr` output by default; do not use `npx @pierre/diffs`; if unavailable, include a clearly labeled plain diff/code block and note why)
+5. **Relevant Diff Snippets** (render with Clean SSR `@pierre/diffs/ssr` output inside the template's `.diff-view` by default; do not use `npx @pierre/diffs`; if unavailable, include a clearly labeled plain fallback diff block and note why)
6. **Expected Impact for End-Users**
7. **Validation**
8. **Issues, Limitations, and Mitigations**
diff --git a/docs/turns/2026-05-25-show-opensubtitles-languages-in-caption-tracks.html b/docs/turns/2026-05-25-show-opensubtitles-languages-in-caption-tracks.html
index 2dfb66f..5354d29 100644
--- a/docs/turns/2026-05-25-show-opensubtitles-languages-in-caption-tracks.html
+++ b/docs/turns/2026-05-25-show-opensubtitles-languages-in-caption-tracks.html
@@ -4,428 +4,690 @@
Show OpenSubtitles Languages in Caption Tracks
+
+
+
-
-
-
Dreamio Turn Document · May 25, 2026
-
Show OpenSubtitles Languages in Caption Tracks
-
External subtitles attached through VLC now keep Dreamio's parsed language metadata, so the captions menu can show useful names like English, Spanish, or English SDH instead of generic VLC labels such as Track 1.
External subtitles attached through VLC now keep Dreamio's parsed language metadata, so the captions menu can show useful names like English, Spanish, or English SDH instead of generic VLC labels such as Track 1.
+
+ May 25, 2026
+ Beads issue dreamio-2ju
+ Subtitle display names
+ Tests and simulator build passed
+
+
+
+
+
+
Summary
+
Dreamio already parsed OpenSubtitles labels and language codes, but VLC exposed attached external subtitle tracks later with generic names. This change preserves each external candidate's best display name, maps it onto the new VLC track ID once it appears, and returns the preserved name whenever VLC's reported name is generic or empty.
+
+
+
+
Changes Made
+
+
Added SubtitleDisplayName, an internal helper for turning subtitle labels and language metadata into menu-ready names.
+
Added VLC backend state for pending external subtitle display names and track-ID-to-display-name mappings.
+
Updated subtitleTracks so generic VLC names are replaced only when Dreamio has a preserved external subtitle name for that specific track ID.
+
Added unit coverage for language-code normalization, meaningful labels, filename fallback, and menu option names.
+
+
+
+
+
Context
+
OpenSubtitles and Stremio payloads often carry useful metadata such as eng, Spanish, or English SDH. The parser kept that metadata on SubtitleCandidate, but once addPlaybackSlave handed a URL to VLC, the eventual track list could contain only Track 1, Track 2, or an empty string. The captions menu reads from backend.subtitleTracks, so the safest fix was to improve that backend output.
+
+
+
+
Important Implementation Details
+
+
Labels are trusted when they are meaningful. English SDH stays English SDH.
+
Generic labels such as Track 1, External Subtitle, empty strings, and numeric-only labels fall back to language metadata.
+
Common language codes are normalized explicitly, including eng/en, spa/es, and fre/fra/fr. The helper also uses Locale for broader short-code support.
+
The VLC backend captures baseline subtitle track IDs before attachment, then assigns preserved names to newly visible external track IDs in attachment order.
+
Embedded tracks are left alone unless they match a newly mapped external ID and VLC reports a generic or empty name.
Diffs are generated with @pierre/diffs/ssr at documentation time. Each snippet is intentionally scoped and contained inside its own diff shell so the page remains readable as a static HTML artifact.
+
+
+
+
Expected Impact for End-Users
+
Users should see clearer captions menu choices after external OpenSubtitles tracks attach. Instead of guessing between Track 1 and Track 2, they should see language-aware labels that make track selection understandable at a glance.
Manual app verification with a live OpenSubtitles playback source was not performed in that turn.
+
+
+
+
+
Issues, Limitations, and Mitigations
+
+
The external ID mapping assumes VLC exposes newly attached subtitle tracks in the same order Dreamio attached them. The mapping is constrained to tracks outside the captured baseline.
+
If VLC later returns a meaningful track name for an external subtitle, Dreamio keeps VLC's name instead of overriding it.
+
The language helper covers common OpenSubtitles/Stremio codes directly and delegates broader short-code support to Locale.
+
+
+
+
+
Follow-up Work
+
+
No required follow-up was filed for dreamio-2ju.
+
A useful future improvement would be a lightweight integration test seam around VLC subtitle-track reconciliation, if the backend gets a mockable media-player adapter later.
+
+
-
-
-
-
Summary
-
Dreamio already parsed OpenSubtitles labels and language codes, but VLC exposed attached external subtitle tracks later with generic names. This change preserves each external candidate's best display name, maps it onto the new VLC track ID once it appears, and returns the preserved name whenever VLC's reported name is generic or empty.
-
-
-
-
Changes Made
-
-
Added SubtitleDisplayName, an internal helper for turning subtitle labels and language metadata into menu-ready names.
-
Added VLC backend state for pending external subtitle display names and track-ID-to-display-name mappings.
-
Updated subtitleTracks so generic VLC names are replaced only when Dreamio has a preserved external subtitle name for that specific track ID.
-
Added unit coverage for language-code normalization, meaningful labels, filename fallback, and menu option names.
-
-
-
-
-
Context
-
OpenSubtitles and Stremio payloads often carry useful metadata such as eng, Spanish, or English SDH. The parser kept that metadata on SubtitleCandidate, but once addPlaybackSlave handed a URL to VLC, the eventual track list could contain only Track 1, Track 2, or an empty string. The captions menu reads from backend.subtitleTracks, so the safest fix is to improve that backend output.
-
-
-
-
Important Implementation Details
-
-
Labels are trusted when they are meaningful. English SDH stays English SDH.
-
Generic labels such as Track 1, External Subtitle, empty strings, and numeric-only labels fall back to language metadata.
-
Common language codes are normalized explicitly, including eng/en, spa/es, and fre/fra/fr. The helper also uses Locale for broader short-code support.
-
The VLC backend captures baseline subtitle track IDs before attachment, then assigns preserved names to newly visible external track IDs in attachment order.
-
Embedded tracks are left alone unless they match a newly mapped external ID and VLC reports a generic or empty name.
Users should see clearer captions menu choices after external OpenSubtitles tracks attach. Instead of guessing between Track 1 and Track 2, they should see language-aware labels that make track selection understandable at a glance.
Manual app verification with a live OpenSubtitles playback source was not performed in this turn.
-
-
-
-
Issues, Limitations, and Mitigations
-
-
The external ID mapping assumes VLC exposes newly attached subtitle tracks in the same order Dreamio attached them. This matches the intended flow and is constrained to tracks outside the captured baseline.
-
If VLC later returns a meaningful track name for an external subtitle, Dreamio keeps VLC's name instead of overriding it.
-
The language helper covers common OpenSubtitles/Stremio codes directly and delegates broader short-code support to Locale.
-
-
-
-
-
Follow-up Work
-
No required follow-up was filed. A useful future improvement would be a lightweight integration test seam around VLC subtitle-track reconciliation, if the backend gets a mockable media-player adapter later.