docs(skills): make captions non-optional in changelog-video#2729
Conversation
The Jul 13-20 build shipped without captions because the pipeline had two soft failure modes: - Empty vo-words.json from the TTS provider was silently accepted, so align-captions.mjs had nothing to align. captions.json was never produced. - Step 5 tolerated the master-skeleton's LINES array staying empty. The build lint-checked and validated clean, but the caption rail rendered no text. This adds three hard gates on top of the existing scaffold: - Step 4 adds a whisper forced-alignment fallback so a missing TTS timestamp payload no longer breaks the caption pipeline. Whisper only supplies timings; captions still use the DISPLAY layer from script-tokens.json. - Step 5 flags an empty LINES array as a shipped bug and shows the exact IIFE-input shape it expects. - Step 6 adds gate 5: sample 3-4 frames across the VO window and confirm visible caption text on each. If any spoken window renders no caption, the build is red. Anti-patterns table gets two rows covering the empty-LINES and missing-word-timings failure modes. .claude and .agents mirrors stay byte-identical. Follow-up to #2669 (pre-build gate); paired with the v5 rebuild that retrofitted captions onto the shipped Jul 13-20 video. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…gibility 25px reads too small at 1080² when the video is viewed on a phone without zoom. Bumped to 32px (font-size 25→32, top 1002→990, height 40→52, alpha .92→.94) so captions carry across small-screen playback. Change is CSS-only on the master-skeleton; existing per-scene chrome and layouts are untouched. Retrofitted the Jul 13-20 v6 build with the same values (identical CSS block) so the shipped video and the future default match. .claude and .agents mirrors stay byte-identical. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
miga-heygen
left a comment
There was a problem hiding this comment.
SSOT Review: weekly changelog digest + captions non-optional in changelog-video skill
Combined review: #2664 (1 file, +45/-0) + #2729 (4 files, +118/-30)
SSOT inventory
#2664 — weekly changelog digest
Content-only docs entry — no SSOT structures to inventory.
#2729 — captions non-optional
| Concept | Source of truth | Consumers | Verdict |
|---|---|---|---|
| SKILL.md (caption gates + anti-patterns) | .claude/skills/changelog-video/SKILL.md |
.agents/skills/changelog-video/SKILL.md (mirror) |
Mirrors match ✓ |
| Master-skeleton caption CSS | .claude/skills/changelog-video/examples/master-skeleton.html |
.agents/skills/changelog-video/examples/master-skeleton.html (mirror) |
Mirrors match ✓ |
| Caption rail position | master-skeleton.html #cap-line { top: 990px } |
SKILL.md gate 5 reference | STALE — see Blocking Issues |
What I checked
- Video embed.
<Frame><video controls muted playsinline src="...weekly-changelog-jul13-20.mp4">— matches the CDN URL discussed in the thread (v6 with 32px captions, CloudFront invalidated). ✓ - Content structure. Features → Fixes → Docs & Examples sections. Each entry has commit SHA + PR link. Reads clean. ✓
- Mintlify
<Update>tags.label,description,tagspresent. Matches the existing format pattern (prepended per the{/* ... */}comment). ✓
-
Mirror sync. Both
.claude/skills/and.agents/skills/copies ofSKILL.mdhave identical diffs. Both copies ofmaster-skeleton.htmlhave identical diffs. ✓ -
Word-timings hard gate (step 4). Verifies
vo-words.jsonis non-empty with awordsarray before proceeding to step 5. Whisper fallback for the known TTS failure mode (audio but no timestamps). Notes that whisper mishears TTS renderings — captions use DISPLAY spelling, whisper supplies only timestamps. Correct. ✓ -
Captions non-optional gate (step 5).
LINESarray must be populated fromcaptions.jsonbefore step 6. "Leaving that array empty is a shipped bug, not a style choice." Correct — this was the v4 failure. ✓ -
Caption presence gate (step 6, gate 5). Sample 3-4 frames across the VO's spoken window, confirm the caption rail renders visible text on each. Hard fail if missing. ✓
-
Anti-patterns table. Two new entries: "Shipping with the
LINESarray empty" and "Novo-words.json→ skip captions." Both point at the correct fix steps. Table formatting is consistent. ✓ -
Master-skeleton CSS changes. Caption rail:
font-size: 25px → 32px,top: 1002px → 990px,height: 40px → 52px,color opacity: .92 → .94. The 32px matches the v6 build that James approved in the thread. ✓
Blocking SSOT issues
1. Stale caption rail position in SKILL.md gate 5.
SKILL.md step 6, gate 5 reads:
confirm the caption rail at
top: 1002renders visible text on each.
But this same PR changes the master-skeleton's #cap-line to top: 990px. An agent following the skill would reference the wrong position. Should read top: 990.
Verdict
#2664: Approve. Clean docs entry.
#2729: Changes Requested — one stale position reference. Fix the top: 1002 → top: 990 in both SKILL.md mirrors (.claude/ and .agents/), step 6 gate 5, and this is clean.
--- Miga
…+ build-spec Miga caught: the master-skeleton bump to top: 990 / 32px / height: 52 was not reflected in the SKILL.md prose or build-spec.md. All three files now name the same values so the doc, gate, and code stay consistent. Also clarifies build-spec.md's caption-rail line to point at SKILL.md step 5 as the source of the LINES-populated requirement. .claude and .agents mirrors byte-identical. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
miguel-heygen
left a comment
There was a problem hiding this comment.
The original SKILL.md drift is fixed, and the three edited mirror pairs are byte-identical. One SSOT consumer remains stale:\n\nblocker — and still specify and . The updated explicitly says the caption rail is “per ” while specifying , , and , so agents still receive contradictory canonical instructions. Update both mirrors to the new values; the repo-wide skill grep is otherwise clean.\n\nVerdict: REQUEST CHANGES\nReasoning: The reported gate/build-spec drift is repaired, but its directly referenced caption-layout source still carries the old position and font size, leaving the same SSOT failure mode active.\n\n— Magi
miguel-heygen
left a comment
There was a problem hiding this comment.
Correction with the exact paths stripped from my submitted review body:
blocker — .claude/skills/changelog-video/references/script-voice.md:108 and .agents/skills/changelog-video/references/script-voice.md:108 still specify top: 1002px and 25px. The updated build-spec.md:79 explicitly says the caption rail is “per script-voice.md” while specifying top: 990, 32px, and 52px, so agents still receive contradictory canonical instructions. Update both script-voice.md mirrors to the new values; the repo-wide skill grep is otherwise clean.
Verdict: REQUEST CHANGES
Reasoning: The reported gate/build-spec drift is repaired, but its directly referenced caption-layout source still carries the old position and font size, leaving the same SSOT failure mode active.
— Magi
vanceingalls
left a comment
There was a problem hiding this comment.
LGTM — approving at head dd9be97 (post-Miga-fix).
Convergence with Miga's SSOT audit. My independent lens surfaced the same P1 he did — top: 1002 reference in SKILL.md step 6 gate 5 was stale against the master-skeleton.html change to top: 990px. Rames pushed the fix commit dd9be971817e ~1 min after Miga's review posted, and it's clean:
.claude/skills/changelog-video/SKILL.md— line 42 + line 161 both citetop: 990✓.agents/skills/changelog-video/SKILL.md— same, mirror byte-identical ✓references/build-spec.md× 2 mirrors — updated totop: 990, font-size: 32px, height: 52✓master-skeleton.html× 2 mirrors —#cap-line { top: 990px; height: 52px; font-size: 32px; ... }✓
Rest of the diff LGTM:
- Caption size
25px → 32pxmatches the v6 James approved in the thread. - Step 4 whisper forced-alignment fallback closes the "audio but no timestamps" failure mode; DISPLAY-spelling-from-tokens rule is called out (whisper only supplies timings, not text).
- Step 5 LINES-population example is unambiguous — empty array is called out as "a shipped bug, not a style choice."
- Step 6 gate 5 is the right shape (sample 3-4 frames across the VO's spoken window, hard-fail on missing text).
- Anti-patterns table gains the two rows that would have caught the v4 uncaptioned ship.
No further asks.
— Via
Third and final stale reference to top: 1002 / 25px — script-voice.md was still stating the pre-bump values. Now matches master-skeleton.html, build-spec.md, and SKILL.md. Also bumps the 'keep critical small text out' guidance from ~80px to ~100px to reflect the slightly taller caption rail. Full-directory sweep confirms zero remaining occurrences of 1002 or 25px across .claude/ and .agents/ skill mirrors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
miguel-heygen
left a comment
There was a problem hiding this comment.
Freshness review at bdc2b96:
- The requested script-voice.md fix is present in both mirrors: top 990px, height 52px, 32px, and .94 ink.
- Repo-wide grep under both changelog-video skill trees finds zero remaining top 1002 or 25px references.
- SKILL.md, master-skeleton.html, build-spec.md, and script-voice.md are byte-identical across .claude and .agents (matching SHA-256 for every pair).
- Required regression and semantic-title checks pass; path-filtered build/test jobs correctly skip.
The SSOT blocker is fully resolved.
Verdict: APPROVE
Reasoning: All caption-layout sources and mirrored consumers now agree on the same rail contract, and the exact-head required checks are green.
— Magi
vanceingalls
left a comment
There was a problem hiding this comment.
Re-stamp at head bdc2b96 (my prior APPROVE at dd9be97 was auto-dismissed by Rames's post-Magi fix push under require_last_push_approval).
n=3 lens convergence on SSOT drift. Miga (SSOT lens, at 0affee5) → me (cross-lens fresh scan, at dd9be97) both hit SKILL.md + build-spec.md residuals of top: 1002. Magi (adversarial lens, at dd9be97) then hit references/script-voice.md:108 — where the caption-rail spec is cited by build-spec.md as the SoT ("caption rail per script-voice.md") but still carried the old 1002/25px values. Real P1 that I missed on my first pass because my grep scoped to what MY subagent thought were the canonical files, not the doc chain build-spec explicitly points at. Fair catch.
Verified at bdc2b96 — 8 files in the diff (SKILL.md × 2 mirrors, master-skeleton.html × 2, build-spec.md × 2, script-voice.md × 2):
- Zero residual
1002or25pxrefs across any of them. - 990 / 32px / 52px consistent everywhere.
- ink .92 → .94 propagated to script-voice.md line 109.
- "bottom ~80px" → "bottom ~100px" reserved-band note updated in script-voice.md consistent with the taller caption rail.
- Both mirrors byte-identical.
Rest of the substantive diff (whisper forced-alignment step 4 gate, LINES-mandatory step 5, sample-3-4-frames step 6 gate 5, two new anti-pattern rows) is unchanged from my prior read — still LGTM.
Magi's block is stale post-fix. Rames — nudge Magi to re-review at bdc2b96 so his CHANGES_REQUESTED lifts, or ask vance to dismiss his review as branch-admin. Once that clears + James is happy, this is mergeable.
— Via
Description
Follow-up to #2669 (which added Step 0 the pre-build asset gate). The Jul 13-20
weekly changelog video shipped without captions because the pipeline had two
soft failure modes that both passed lint + validate:
payload. `align-captions.mjs` silently produced an empty `captions.json`.
ships with a placeholder `const LINES = /* … */ []` that stays empty when
`captions.json` is missing. Nothing in the pipeline caught this.
Result: a build that lint-clean-passed, validate-clean-passed, and rendered a
clean 34 MB MP4 — with no captions. Jake and James caught it after ship.
This PR adds three hard gates on top of the existing scaffold — no new
infrastructure, just closing the loopholes:
timestamp payload no longer breaks the caption pipeline. Whisper only
supplies timings; captions still use the DISPLAY layer from
`script-tokens.json`.
exact IIFE-input shape it expects.
confirm visible caption text on each. If any spoken window renders no
caption, the build is red.
Anti-patterns table gets two rows covering both failure modes.
`.claude/skills` and `.agents/skills` mirrors stay byte-identical after
the update.
Testing
fallback produced 27 caption cues; render was byte-clean; caption presence
verified at t=3.5s, t=9s, t=17s, t=25s, t=35s, t=44s.
`https://static.heygen.ai/hyperframes/changelog-videos/weekly-changelog-jul13-20.mp4\`.
— Rames Jusso