Skip to content

docs(spf): seed feature registry, clusters reference, and document-feature skill#1581

Merged
cjpillsbury merged 59 commits into
mainfrom
docs/spf-feature-docs-and-skills
May 21, 2026
Merged

docs(spf): seed feature registry, clusters reference, and document-feature skill#1581
cjpillsbury merged 59 commits into
mainfrom
docs/spf-feature-docs-and-skills

Conversation

@cjpillsbury
Copy link
Copy Markdown
Collaborator

@cjpillsbury cjpillsbury commented May 21, 2026

Why

SPF design context has lived in PR descriptions, Notion working docs, and code comments — fine when one person needed to find it, brittle as more features and more contributors land. This PR seeds a permanent registry for that context, plus the skill that maintains it.

The primary reader is an LLM, with a human in the loop. These docs are written to be consumed by and updated by LLMs (via /spf-document-feature today, and the upcoming implementation skills) while doing SPF feature and use-case development. Humans are a secondary audience — code review, design review, and onboarding still benefit, but the shape is optimized for grounding agent work: structured frontmatter, predictable section ordering, explicit cluster + classification-axis tagging, heavy cross-refs into conventions/* and sibling features/*, and a narrow-cascade rule so cross-doc context doesn't drift.

What this enables:

  • Permanent home for feature-level design context — what each feature is, what it gates on, where it sits in the architecture. Survives PR-description rot and Notion churn.
  • Foundation for the next round of skillsspf-implement-feature, spf-create-behavior, spf-document-use-case, spf-implement-use-case will read this registry as their scoping/grounding layer. Today's docs are forward enablement, not just retrospective documentation.
  • Decomposition discipline at scoping timeclusters.md + classification axes (Media-src/Player/Borderline, Naive/Full) give us shared vocabulary for the feature-vs-phase call instead of re-litigating it each invocation. The skill applies these heuristics consistently, triangulates across Notion / GitHub / code, and surfaces alternative framings when criteria fire weakly.

Workflow change going forward — new SPF features route through /spf-document-feature first (registry entry or a deepening of an existing one), so the cluster + cross-cutting concern checks run at scoping time before code lands.

What's in this PR

  • Feature registry — 25 docs under internal/design/spf/features/ covering clusters A–G:
    • Media-src foundations: live-stream-support, multi-language-audio, container-support, non-zero-pts-support, capability-probing, engine-adapter-integration, mse-mms-pipeline, source-replacement, buffer-management, preload-modes, audio-playback, subtitles, dvr-event-stream-support, ll-hls-support
    • Rendition / ABR: video-abr, audio-abr, multi-signal-abr, rendition-selection-caps, hevc-variant-selection, 5.1-surround-selection
    • Borderline content compensation: edit-list-compensation, pseudo-ended-detection, buffer-stall-recovery
    • Resilience / DRM / steering: network-resilience, multi-cdn-failover, content-steering, drm-support
    • Composition modes: audio-only-composition, video-only-composition
  • Cross-cluster references:
    • features/clusters.md — cluster heuristics, cross-cluster patterns, classification axes
    • presentation-modeling.md — architectural deep-dive on the presentation slot lifecycle
  • Skill.claude/skills/spf-document-feature/ drives source gathering, weak-criterion decomposition checks, cross-cutting concern checks, definition-depth selection, and narrow cross-doc cascade

Small code-adjacent updates that landed alongside the docs:

  • packages/spf/docs/hls-engine.md — refresh trackPlaybackInitiatedtrackLoadTriggers and cross-ref features/preload-modes.md
  • packages/spf/src/playback/behaviors/dom/setup-buffer-actors.ts — JSDoc cross-ref into features/audio-abr.md (comment-only)
  • packages/spf/src/playback/engines/hls/tests/engine.test.ts — initial-state-seeding coverage for createSimpleHlsEngine

Test plan

  • internal/design/spf/features/clusters.md renders correctly on GitHub (tables, headings)
  • Spot-check internal links in a handful of feature docs (conventions/*, sibling features/*)
  • pnpm -F @videojs/spf test src/playback/engines/hls/tests/engine.test.ts passes
  • pnpm typecheck and pnpm lint clean

🤖 Generated with Claude Code


Note

Low Risk
Primarily adds/updates internal design documentation and a Claude skill; functional runtime risk is minimal aside from potential reviewer time and link/consistency drift across many new docs.

Overview
Seeds an SPF feature registry under internal/design/spf/features/ with new/updated feature docs (including selection/ABR, capability probing, buffer management, DRM, steering, and composition modes) and a new clusters.md reference that defines cluster heuristics, classification axes, and cross-cluster patterns.

Adds a new Claude skill, spf-document-feature (.claude/skills/spf-document-feature/SKILL.md), that prescribes a structured workflow to create/update feature-registry entries (source triangulation, decomposition checks, code grounding, cross-cutting concern checks, and narrow cross-doc cascades).

Reviewed by Cursor Bugbot for commit e1eeebb. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment May 21, 2026 5:55pm

Request Review

@netlify
Copy link
Copy Markdown

netlify Bot commented May 21, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit e1eeebb
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a0f470440197c0008742c5a
😎 Deploy Preview https://deploy-preview-1581--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

📦 Bundle Size Report

🎨 @videojs/html — no changes
Presets (7)
Entry Size
/video (default) 38.57 kB
/video (default + hls) 172.02 kB
/video (minimal) 38.10 kB
/video (minimal + hls) 171.55 kB
/audio (default) 33.08 kB
/audio (minimal) 30.01 kB
/background 4.23 kB
Media (8)
Entry Size
/media/background-video 1.04 kB
/media/container 1.72 kB
/media/dash-video 236.58 kB
/media/hls-video 134.87 kB
/media/mux-audio 161.08 kB
/media/mux-video 161.00 kB
/media/native-hls-video 4.62 kB
/media/simple-hls-video 16.70 kB
Players (5)
Entry Size
/video/player 7.06 kB
/audio/player 5.18 kB
/background/player 3.92 kB
/live-video/player 7.07 kB
/live-audio/player 5.19 kB
Skins (30)
Entry Type Size
/video/minimal-skin.css css 4.67 kB
/video/skin.css css 4.73 kB
/video/minimal-skin js 38.02 kB
/video/minimal-skin.tailwind js 38.44 kB
/video/skin js 38.58 kB
/video/skin.tailwind js 38.99 kB
/audio/minimal-skin.css css 2.90 kB
/audio/skin.css css 2.93 kB
/audio/minimal-skin js 29.94 kB
/audio/minimal-skin.tailwind js 30.17 kB
/audio/skin js 33.09 kB
/audio/skin.tailwind js 33.28 kB
/background/skin.css css 133 B
/background/skin js 1.16 kB
/live-video/minimal-skin.css css 4.67 kB
/live-video/skin.css css 4.73 kB
/live-video/minimal-skin js 33.19 kB
/live-video/minimal-skin.tailwind js 33.24 kB
/live-video/skin js 33.34 kB
/live-video/skin.tailwind js 33.28 kB
/live-audio/minimal-skin.css css 2.90 kB
/live-audio/skin.css css 2.93 kB
/live-audio/minimal-skin js 25.19 kB
/live-audio/minimal-skin.tailwind js 24.99 kB
/live-audio/skin js 27.86 kB
/live-audio/skin.tailwind js 27.63 kB
/global.css css 176 B
/shared.css css 88 B
/tailwind.css css 228 B
/skin-element js 1.37 kB
UI Components (35)
Entry Size
/ui/alert-dialog 713 B
/ui/alert-dialog-close 363 B
/ui/alert-dialog-description 307 B
/ui/alert-dialog-title 309 B
/ui/buffering-indicator 2.11 kB
/ui/captions-button 2.08 kB
/ui/cast-button 2.08 kB
/ui/compounds 5.53 kB
/ui/controls 1.99 kB
/ui/error-dialog 2.51 kB
/ui/fullscreen-button 2.14 kB
/ui/hotkey 2.77 kB
/ui/menu 2.78 kB
/ui/mute-button 2.10 kB
/ui/pip-button 2.07 kB
/ui/play-button 2.08 kB
/ui/playback-rate-button 2.19 kB
/ui/playback-rate-menu 3.90 kB
/ui/popover 1.87 kB
/ui/poster 1.87 kB
/ui/seek-button 2.07 kB
/ui/seek-indicator 2.71 kB
/ui/seek-indicator-value 306 B
/ui/slider 1.19 kB
/ui/status-announcer 2.47 kB
/ui/status-indicator 2.50 kB
/ui/status-indicator-value 159 B
/ui/thumbnail 2.54 kB
/ui/time 1.92 kB
/ui/time-slider 3.04 kB
/ui/tooltip 1.79 kB
/ui/volume-indicator 2.74 kB
/ui/volume-indicator-fill 335 B
/ui/volume-indicator-value 328 B
/ui/volume-slider 3.56 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react — no changes
Presets (7)
Entry Size
/video (default) 32.29 kB
/video (default + hls) 164.70 kB
/video (minimal) 32.33 kB
/video (minimal + hls) 164.64 kB
/audio (default) 26.57 kB
/audio (minimal) 26.60 kB
/background 754 B
Media (7)
Entry Size
/media/background-video 575 B
/media/dash-video 235.21 kB
/media/hls-video 133.39 kB
/media/mux-audio 159.84 kB
/media/mux-video 159.74 kB
/media/native-hls-video 3.13 kB
/media/simple-hls-video 15.27 kB
Skins (27)
Entry Type Size
/tailwind.css css 228 B
/video/minimal-skin.css css 4.58 kB
/video/skin.css css 4.64 kB
/video/minimal-skin js 32.27 kB
/video/minimal-skin.tailwind js 37.00 kB
/video/skin js 32.19 kB
/video/skin.tailwind js 37.04 kB
/audio/minimal-skin.css css 2.77 kB
/audio/skin.css css 2.80 kB
/audio/minimal-skin js 26.54 kB
/audio/minimal-skin.tailwind js 26.25 kB
/audio/skin js 26.46 kB
/audio/skin.tailwind js 29.52 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 4.58 kB
/live-video/skin.css css 4.64 kB
/live-video/minimal-skin js 23.69 kB
/live-video/minimal-skin.tailwind js 28.15 kB
/live-video/skin js 23.74 kB
/live-video/skin.tailwind js 28.15 kB
/live-audio/minimal-skin.css css 2.77 kB
/live-audio/skin.css css 2.80 kB
/live-audio/minimal-skin js 19.95 kB
/live-audio/minimal-skin.tailwind js 22.38 kB
/live-audio/skin js 20.00 kB
/live-audio/skin.tailwind js 22.52 kB
UI Components (29)
Entry Size
/ui/alert-dialog 1.08 kB
/ui/buffering-indicator 1.86 kB
/ui/captions-button 2.08 kB
/ui/cast-button 2.04 kB
/ui/controls 1.84 kB
/ui/error-dialog 2.27 kB
/ui/fullscreen-button 2.08 kB
/ui/gesture 1.32 kB
/ui/hotkey 1.90 kB
/ui/live-button 2.10 kB
/ui/menu 4.29 kB
/ui/mute-button 2.03 kB
/ui/pip-button 2.04 kB
/ui/play-button 2.06 kB
/ui/playback-rate-button 2.04 kB
/ui/playback-rate-menu 4.61 kB
/ui/popover 2.32 kB
/ui/poster 1.69 kB
/ui/seek-button 2.07 kB
/ui/seek-indicator 1.87 kB
/ui/slider 3.22 kB
/ui/status-announcer 1.76 kB
/ui/status-indicator 1.96 kB
/ui/thumbnail 1.95 kB
/ui/time 2.51 kB
/ui/time-slider 2.97 kB
/ui/tooltip 2.72 kB
/ui/volume-indicator 1.98 kB
/ui/volume-slider 2.30 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (9)
Entry Size
. 7.47 kB
/dom 15.30 kB
/dom/media/custom-media-element 1.90 kB
/dom/media/dash 234.36 kB
/dom/media/google-cast 4.07 kB
/dom/media/hls 132.98 kB
/dom/media/mux 159.13 kB
/dom/media/native-hls 2.52 kB
/dom/media/simple-hls 14.62 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 996 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 696 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 1.96 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 192 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (3)
Entry Size
. 4.45 kB
/dom 6.32 kB
/hls 14.03 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

cjpillsbury and others added 19 commits May 21, 2026 06:00
…guage-audio

Three feature-registry docs at varying definition depths — subtitles
and video-abr at sketched (implementation grounded), multi-language-audio
at coarse (candidate next-priority feature). Establishes the template
structure by extracting from real work rather than designing upfront.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Heuristics doc for grouping SPF features by area of state and behavior.
9 clusters (track registry, selection policy, presentation modeling,
MSE / buffer management, time normalization, manifest reload, capability
probing, selection resilience, DRM) plus 5 cross-cluster patterns
(gating, multi-writer state slots, constraint+filter, per-type
specialization, sampling-baked-into-loading). Consumed by the
spf-document-feature skill at steps 1, 3, 4, 5, and 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v1 skill for producing or updating SPF feature-registry docs at
internal/design/spf/features/. Nine steps with explicit failure-mode
catalog, references features/clusters.md throughout. Structure modeled
on the refactor-behavior skill.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add sketched-depth feature doc for the MSE / Managed Media Source
pipeline: MediaSource lifecycle, per-type SourceBuffer + actor setup
(with the Firefox mozHasAudio invariant), MMS-on-Safari attach branch,
initial duration write, and end-of-stream coordination. Scope ends at
the lifecycle boundary; segment-loading orchestration and buffer-flush
orchestration remain sibling features.

Cascade refs:
- multi-language-audio.md: link the Audio SourceBuffer flush on switch
  bullet to mse-mms-pipeline (where the remove/flushBuffer primitives
  live) and add a Related features entry for the lifecycle dependency.
- video-abr.md: See also link noting that setupVideoBufferActors is
  where createTrackedFetch is wired (sampling co-located with MSE setup,
  ABR is the consumer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add sketched-depth feature doc for preload-modes: the engine's
loading-semantics implementation pairing syncPreload (bidirectional
state.preload <-> mediaElement.preload sync, sticky extended values)
with trackLoadTriggers (DOM play/seeking flips state.loadActivated).
Together these mirror native HTMLMediaElement preload behavior; the
state.loadActivated slot is multi-writer with the adapter's
programmatic play() path.

Cascade refs:
- mse-mms-pipeline.md: preload-modes gates indirectly via
  resolvePresentation.
- subtitles.md: loadTextTrackSegments consumes the same (preload,
  loadActivated) gate state.
- video-abr.md: See also link noting indirect gating.

Engine walkthrough fix: hls-engine.md Stage 1 referenced the precursor
trackPlaybackInitiated behavior writing state.playbackInitiated; the
current code is trackLoadTriggers writing state.loadActivated. Updated
the walkthrough to match and cross-link the new feature doc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add sketched-depth feature doc for buffer-management: the engine's
segment-level buffer policy (forward-buffer planning, back-buffer
eviction, in-flight continue/preempt, quality-aware filter, seek
handling, streaming append). Covers loadVideoSegments /
loadAudioSegments / loadTextTrackSegments dispatchers, the v/a
SegmentLoaderActor + TextTrackSegmentLoaderActor, and the
forward-buffer / back-buffer policy primitives in media/buffer/.

Absorbs the [per-track-segment-loading] bracketed candidate from
clusters.md — that was an architectural slice, not a feature; the
engine behaviors it described all live here.

Captures candidate phases as What's not implemented: smarter
forward-buffer sizing (playback-rate-aware, canplaythrough-equivalent,
network/seek-density-aware); smarter back-buffer eviction
(quota-learning that records observed QuotaExceededError thresholds
for proactive use, time/size-based variants); loop-aware fetching for
loop=true; changeType() codec-change transitions; buffer health
observability.

Cascade refs: replace 5 per-track-segment-loading references across
mse-mms-pipeline.md, multi-language-audio.md, preload-modes.md,
subtitles.md, video-abr.md with buffer-management. In preload-modes
the per-track-segment-loading and (already-bracketed) buffer-management
entries collapse to a single updated entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add sketched-depth feature doc for source-replacement: the engine's
source-change capability via in-place state.presentation overwrite.
resolvePresentation routes the FSM back through 'resolving' on URL
change; downstream behaviors (setupMediaSource, setupVideoBufferActors,
setupAudioBufferActors, endOfStream, resolveXTrack, etc.) tear down
via reactor state-exit. Same engine instance survives the cascade — no
destroy required.

The cleanup contract is load-bearing: new behaviors that gate on
isResolvedPresentation MUST honor state-exit cleanup or in-place
replacement breaks silently. The canonical adapter (SimpleHlsMediaMixin)
chose destroy-and-recreate instead, which bypasses the cascade entirely
— so the in-place path is the engine's load-bearing capability for
internal reasoning.

Add a new "cleanly replaces source in place via state.presentation
overwrite" test in engine.test.ts that validates the cascade end-to-end
(MediaSource and bufferActor identities differ between sources A and B,
proving full teardown). Pins the cleanup contract against regression.

Add a new "Engine lifecycle" cluster in clusters.md grouping
preload-modes + source-replacement + the [engine-adapter-integration]
candidate. The cluster's Key check captures the cleanup-contract
requirement for new presentation-gated behaviors.

Cascade refs: cross-link source-replacement from preload-modes.md,
mse-mms-pipeline.md, and buffer-management.md so future contributors
working in those areas surface the contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add sketched-depth feature doc for audio-playback: today's
single-rendition audio playback in the HLS engine. Covers rendition
recognition, default picker (pickFirstTrackId today), media playlist
resolution, SourceBuffer setup (architecturally owned by
mse-mms-pipeline), and segment loading (architecturally owned by
buffer-management). Sibling of subtitles, video-abr, and the future
multi-language-audio.

Notable finding flagged in What's not implemented + Open questions:
the language-aware pickAudioTrack picker exists in media/primitives/
but isn't wired as the default in selectAudioTrack. preferredAudioLanguage
is exposed on the engine config but inert with the default picker —
consumers must override SelectAudioTrackConfig.picker to make it take
effect. Wiring pickAudioTrack as the default would be a one-line change.

Cascade refs:
- multi-language-audio.md: Status sentence updated to point at
  audio-playback as the single-rendition baseline; Related features
  entry added.
- clusters.md: audio-playback added to Track & variant registry's docs
  list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add sketched-depth feature doc for engine-adapter-integration: the
engine's external-driving contract pairing the framework-level
shareSignals behavior (writable + readonly signal refs via
onSignalsReady) with the canonical SimpleHlsMediaMixin adapter
(WHATWG HTMLMediaElement-shaped API mapping to engine writes).
Documents the adapter-to-engine state/context mapping (attach/detach,
src destroy-recreate, preload reflection, play with loadstart retry).

Audience is adapter authors and contributors who drive the engine
from outside, not end users. Downstream consumer:
`SimpleHlsMedia extends SimpleHlsMediaMixin(HTMLVideoElementHost)` in
packages/core.

Repeats source-replacement's open question about why the canonical
adapter chose destroy-recreate over the in-place source-replacement
path the engine supports. This is the doc where adapter authors and
new-feature contributors would look for the why.

Cascade: clusters.md Engine lifecycle cluster docs list updated to
drop the [engine-adapter-integration] brackets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add internal/design/spf/presentation-modeling.md as an architectural
deep-dive (peer to text-track-architecture.md) covering the
format-neutral CMAF-HAM data shape, the parser interface, per-track
resolution, and duration resolution. Audience: engine + parser
contributors.

Treats this as architecture rather than a feature doc per the
"features = engine behaviors observable from outside" framing — the
Presentation data shape, resolved/unresolved lifecycle, and parser
contract are foundational infrastructure consumed by every feature, not
an engine behavior in their own right.

Surfaces a load-bearing finding for future format work: the
parseMediaPlaylist parser is imported directly in resolve-track.ts (not
exposed via config), while parsePresentation and resolveDuration ARE
config-pluggable. When DASH/MoQ/short-form work begins, the coupling
needs to either become pluggable via a new config hook or be replaced
by format-specific resolve-track variants.

Cascade: clusters.md "Read alongside" section gains a reference to
this doc and to text-track-architecture.md. Six feature docs
(source-replacement, audio-playback, subtitles, video-abr,
mse-mms-pipeline, buffer-management) get See also cross-refs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add internal/design/spf/features/live-stream-support.md as a
technical-depth feature doc for the manifest-reload-loop cluster (A)
foundation. Covers periodic media-playlist refetch, sliding-window
segment tracking, target-duration pacing, and Infinity duration
semantics. Unblocks sibling features ll-hls-support,
dvr-event-stream-support, live-stream-termination-detection.

First instantiation of the "unimplemented feature doc" pattern: the
doc is structured per the technical-depth section presence (scope +
constraints + likely cross-cutting impact + open questions, no
implementation surface or verification). Cites the SPF Epics Working
Doc and Permutations Matrix as source material; explicitly invokes
the new feature classification axes (Media-src vs Player vs Borderline).

Add Feature classification axes section to clusters.md, pulling
framing from the SPF Epics Working Doc:
- Media-src vs Player vs Borderline (the primary cut for unimplemented work)
- Naive vs Full implementation depth
- "Can play" vs actual support
- Tier 1 (spec-compliant) vs Tier 2 (custom behavior)
- Composition vs Policy vs middle pattern (implementation mechanism)

Drops [live-stream-support] brackets in the cluster A Docs list now
that the doc exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Decomposition decision: termination detection has no use without the
reload loop, modest implementation footprint, and doesn't act as a
primitive other features consume. None of the Step 5 decomposition-
check criteria (substantial independent footprint, independent
priority/timeline, primitive other features consume) fired. Folding
in as phases rather than a sibling feature doc; can split later if
miss-counter heuristic grows independent design surface.

Adds two phase rows to live-stream-support: termination detection
(manifest signal, ENDLIST + miss-counter with naive/full depth) and
terminated state transition (reload-loop exit + downstream
endOfStream reachability + per-type independence).

Corrections from Step 1 grounding the prior reverted draft missed:
- Parser at media/hls/parse-media-playlist.ts:98 recognizes
  EXT-X-ENDLIST via `continue` but does NOT extract the value;
  MediaPlaylistInfo.endList is declared-but-orphaned. The parser-
  side fix is in scope for this feature.
- endOfStream gate uses isLastSegmentAppended + currentTime, not
  presentation.duration finiteness. Prior draft's "Infinity won't
  pass the gate — needs review" claim was wrong. For live, the gate
  naturally doesn't fire (growing playlist, no permanent last
  segment); termination commits the last segment and the gate
  becomes reachable for normal reasons. Possible race if reload
  pacing lags playhead consumption — flagged as open question.

Mux cleanup pass: rewrites unnecessary Mux/Mux Video specifics with
vendor-neutral phrasing in live-stream-support and the new
clusters.md classification axes. Legitimate code-reference citations
to mux-background-video (in buffer-management) and the Permutations
Matrix (in See also) preserved as load-bearing references.

Cluster A docs list: drops [live-stream-termination-detection] entry
(now subsumed into live-stream-support).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add internal/design/spf/features/capability-probing.md as a
technical-depth doc for the cluster D foundation. Covers codec /
container probing (isTypeSupported + canPlayType), multivariant
CODECS filtering, key-system probing (requestMediaKeySystemAccess
for DRM, distinct from EME setup), changeType() availability +
pair-wise transition probing, and the unsupported-case error
surfacing primitive. Tier 1 (spec-compliant filtering) + Tier 2
(customer overrides) layered onto specific phases.

Includes a complexity phase for SourceBuffer.changeType() capability
probing per the scoping discussion — the actual changeType() call
lives in consumer features (HEVC variant selection, 5.1 surround,
multi-language-audio Tier 2), but the "can the browser do this
transition?" probe is here.

Grounding finding: today's only capability check is isCodecSupported
called inside createSourceBuffer — a late-failure path that fires
after selection has already picked a (potentially unsupported)
variant. This feature's main work is moving the check upstream so
unsupported renditions never reach selection; the existing throw
becomes a defensive fallback.

Cascade:
- clusters.md cluster D Docs list collapses [codec-capability-
  filtering], [multivariant-codecs-filtering], [key-system-capability-
  probing] into the single capability-probing doc ([container-
  support] open whether it folds in). Cluster F (DRM) Docs list
  updated to clarify that key-system probing is owned by cluster D
  while EME setup / license handling stay under DRM.
- Related-features cross-refs added to mse-mms-pipeline (late-
  failure path), video-abr (filtered candidate set for ABR), audio-
  playback (filtered candidate set for audio selection), multi-
  language-audio (Tier 2 changeType dependency), and presentation-
  modeling (post-parse consumer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two catalog entries that surfaced during recent feature-doc work:

- **API-as-feature inflation** — single-API invocations (MDN links,
  browser methods) are usually primitives consumed by an existing
  phase, not features in their own right. Pairs with a new Step 1
  decomposition check that defaults to extending an existing doc
  unless the standalone rubric clearly fires. Step 2 now leads with
  the recommendation per AskUserQuestion guidance.

- **Composition-variant logic in always-on behaviors** — when a
  mechanism only applies under one composition variant (live, audio-
  only, DRM-required), it lives as a new behavior composed into that
  variant, not as a runtime conditional in an always-on behavior.
  Worked example: setLiveSeekableRange initially proposed as an
  extension to updateMediaSourceDuration "under Infinity duration";
  corrected to a separate live-only behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends live-stream-support.md's Live edge tracking + Terminated state
transition phase rows to name setLiveSeekableRange / clearLiveSeekableRange
as the DOM-exposure mechanism for the live seekable window. Adds a
cross-cutting-impact bullet on mediaSource.* as a third-writer surface
(structurally distinct from updateMediaSourceDuration + endOfStream) and
an open question on within-live-variant placement (separate reactor vs.
baked into the polling behavior; current lean: separate reactor).

mse-mms-pipeline.md gains a sibling "What's not implemented" bullet and
drops the stale `[live-stream-support]` candidate marker now that the
doc exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously, every anticipated future feature appeared as a bracketed
name in each cluster's Docs line, conflating documented features with
unscoped sibling-feature placeholders. The bracket-as-candidate
convention biased the registry toward "every candidate is a future
feature doc," which short-circuits /spf-document-feature's Step 1
decomposition rubric that decides feature-vs-phase-vs-primitive at
invocation time.

Each cluster's Docs line now lists only documented features; clusters
without any documented features show "None yet." Side fixes that fell
out of the pass: stale [mse-mms-pipeline] bracket dropped (it's
documented) and buffer-management added to the MSE / Buffer management
cluster Docs line (was authored omission).

The Notation section now points out that the unscoped candidates have
been pulled out, and the "How this doc grows" trigger for cluster-list
updates was rewritten to match. The candidates list itself lives in
project memory rather than a checked-in doc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…bles

Same conflation as the prior Docs-list cleanup: bracketed candidates
in the Feature classification axes tables (Media-src/Player/Borderline,
Naive/Full depth, Tier 1/Tier 2, Composition/Policy/middle pattern)
pre-classified unscoped items into axis categories. The mechanism-
category assignment is exactly what /spf-document-feature should
resolve at invocation time, not what cluster docs should assert
upfront.

Each table now keeps documented examples where they exist and replaces
bracketed-candidate examples with descriptive category shapes — the
teaching value (what does each axis category LOOK like?) survives,
without committing specific named candidates to specific categories
before they're scoped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First documented member of the Selection policy cluster. Umbrella
feature unifying the billing-driven max-height cap (e.g., 1080p+),
viewport-driven cap (screen-size / player-size), max-bitrate cap,
and max-FPS cap as motivation phases of a shared constraint+filter
mechanism — `selectQuality` reads per-cap constraint slots and
filters `presentation.videoTracks` before bandwidth-based selection
runs. Builds directly on the constraint+filter pattern that
`video-abr`'s `userVideoTrackSelection` already exercises.

Definition depth technical; not implemented. Source material: SPF
Epics Working Doc cluster E entries #13 (1080p+ Resolution Cap) and
NEW-C (Screen-Size / Player-Size Resolution Cap), unified per that
doc's "Resolution-cap unification" open question. Decomposition
rubric confirmed the umbrella framing — no per-phase criterion
(independent footprint, independent priority, consumed primitive)
fires for 1080p-alone scope.

Cascade: video-abr.md "What's not implemented" + Related features
entries updated to plain links; clusters.md Selection policy cluster
Docs line now lists `rendition-selection-caps`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a fifth phase to rendition-selection-caps.md modeled on Mux
Player's MinCapLevelController: a "minimum effective cap" floor that
counters other caps (primarily viewport-driven) when their combination
would narrow effective candidates below a configured threshold (e.g.,
720p). Structurally distinct from the first four phases — meta-policy
on cap combination, not an upper-bound cap of its own.

Also refines the billing-driven phase Notes with the total-pixel
matching nuance from the same reference (matching by total pixels
ensures exact alignment with resolution-based pricing tiers; height-
based matching is simpler default). Adds cap-combination semantics
bullet to Likely cross-cutting impact, and two new Open questions
(cap-floor scope vs capability-filter; cap-floor default value).

Decomposition rubric confirmed phase-row scope: no independent
implementation footprint (shares the constraint+filter primitive), no
independent timeline, no consumed primitive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cjpillsbury and others added 20 commits May 21, 2026 06:00
Adds the network-resilience feature doc at coarse depth as the cluster
G foundation. Borderline / response-error handling: retry/backoff,
error classification, VRLT-aware response handling, playback-token-
expiry refresh, customer-policy hooks. Tier 1 / Tier 2 framing with
Naive vs Full depth distinctions per phase.

Reframes cluster G: previously framed as "selection resilience" with
multi-CDN as the primary feature. The cluster splits along two axes
now — response-error handling (network-resilience covers this) and
selection-side resilience (multi-cdn-failover / content-steering as
sister consumer features yet to land).

Absorbs:
- selection-retry-backoff candidate (now Tier 1 phase: Generic retry
  with backoff + Error classification + retry budget/circuit-breaker
  in Tier 2)
- Notion epic #12 VRLT (Viewer Rate Limiting Token) as Tier 2 phase
- Notion epic #14 Playback Token Expiry as Tier 2 phase

Six phases total: Generic retry (Tier 1) + Error classification
(Tier 1) + Retry budget/circuit breaker (Tier 2) + VRLT-aware
response (Tier 2) + Playback-token-expiry (Tier 2) + Customer-policy
hooks (Tier 2).

Cross-cutting impact captures: createTrackedFetch composition
(sibling wrapper vs extension), bandwidth-sample exclusion for retry
attempts, retry-policy slot vs per-site config, composition with
multi-cdn-failover (retry vs rotation boundary), circuit-breaker
state ownership, token-refresh hook convergence with DRM license-
refresh, per-fetch-site config (manifest/segment/license/text-
segment), live-stream reload-loop retry semantics.

Open questions left explicit: retry-policy slot, default retry
counts per site, VRLT detection signature shape, token-refresh hook,
bandwidth-sample filtering, circuit-breaker ownership, composition
order with multi-cdn-failover, retry-exhaustion error surfacing,
composition with DRM, composition with content-steering.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrites cluster G description to reflect the new two-axis framing:
response-error handling (network-resilience covers this) and
selection-side resilience (multi-cdn-failover / content-steering as
sister consumer features yet to land).

- Cluster G description: split along two axes; explicit Borderline-
  flavored framing for response-error handling
- Signals: add VRLT, playback-token-expiry, circuit-breaker per host
- Docs: network-resilience now the cluster G foundation
- Foundational primitives: retry/backoff + circuit-breaker primitive
  lives in network-resilience; alternate-URI rotation primitive
  remains for sister consumer features

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the multi-cdn-failover feature doc at coarse depth as the
selection-side resilience sister to network-resilience (response-error
handling). Five phases (Tier 1 / Tier 2 framing):
- Tier 1: alternate-URI parsing + presentation surfacing
- Tier 1: active-URI state + initial selection
- Tier 2: rotation on retry-exhaustion (consumes network-resilience's
  retry-exhaustion signal)
- Tier 2: per-URI health tracking (consumes network-resilience's
  circuit-breaker state)
- Tier 2: customer-policy hooks for rotation override

Hard prerequisite on network-resilience. Mux Video convention noted
(?redundant_streams=true playback URL param). Content-steering kept
as a parallel sister feature (sister-feature framing per user call,
not absorbed) for the dynamic-host-pool case.

Cross-cutting impact captures per-rendition vs per-presentation
active URI, parser surface extension (HLS spec vs Mux convention),
live + multi-CDN composition with reload-loop failover, composition
with content-steering when it lands, rotation state across source
changes, per-stream-type rotation coordination.

Open questions left explicit: manifest syntax (HLS spec vs Mux),
per-rendition vs per-presentation active URI, default rotation
policy, content-steering composition, rotation-state preservation,
customer-hook contract, DRM license-fetch interaction, per-type
rotation coordination.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- network-resilience.md: drop brackets on [multi-cdn-failover] in
  three sites (opening paragraph, Out of scope consumer entry, Related
  features). All now plain-link the new doc.
- clusters.md: cluster G Docs line adds multi-cdn-failover; Foundational
  primitives note updated to clarify alternate-URI rotation primitive
  lives in multi-cdn-failover (consumed by content-steering when it
  lands).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the content-steering feature doc at coarse depth as the dynamic-
host-pool sister to multi-cdn-failover. HLS content-steering protocol
implementation: server advertises pathway-priority via
EXT-X-CONTENT-STEERING; client periodically fetches the steering
manifest and biases URI selection.

Six phases (Tier 1 / Tier 2 framing):
- Tier 1: parse EXT-X-CONTENT-STEERING tag
- Tier 1: initial steering-manifest fetch
- Tier 1: apply pathway-priority to URI selection (composes with
  multi-cdn-failover's rotation)
- Tier 2: periodic steering-manifest refresh (TTL-driven reload loop)
- Tier 2: CDN-RESET-SECONDS handling (server-initiated reset of
  multi-cdn-failover + network-resilience state)
- Tier 2: customer-policy hooks for pathway override

Hard prerequisites on network-resilience (steering-manifest fetch
retries) and multi-cdn-failover (rotation primitive being composed
with). Pathway-priority shape: sort-key bias on multi-cdn-failover's
candidate set (option a from the cross-cutting impact).

Cross-cutting impact captures composition shape with
multi-cdn-failover, reload-loop primitive sharing with
live-stream-support, CDN-RESET-SECONDS state coordination across
three features, per-rendition vs presentation-wide pathway-id,
PATHWAY-CLONES extension scope, steering-manifest authorization,
source-replacement cascade.

Open questions left explicit: composition shape, reload-loop
sharing, per-rendition pathway-id, PATHWAY-CLONES scope,
CDN-RESET-SECONDS write coordination, steering-manifest
authorization, customer-hook contract, mid-stream pathway changes
during live + LL-HLS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- network-resilience.md: drop brackets on [content-steering] in three
  sites (opening paragraph, Out of scope consumer, Related features).
  All plain-link the new doc.
- multi-cdn-failover.md: drop brackets in Out of scope + Related
  features. Composition note in Out of scope refined: pathway-priority
  is a sort-key bias on the rotation primitive (option a from the
  composition open question), not a separate candidate-narrowing slot.
- clusters.md: cluster G Docs line adds content-steering; Foundational
  primitives note updated — alternate-URI rotation primitive lives in
  multi-cdn-failover and is consumed by content-steering's pathway-
  priority bias.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the cluster B foundation doc at technical depth. Time-mapping
primitive for sources where media-segment PTS doesn't start at zero.
Three source-type phases (content-phase framing per user call):

- Non-zero-PTS VOD: re-mastered / processed content retaining
  original PTS rather than zero-rebasing
- Instant clips: deliberate offset for clipping use cases (clip
  starts at original PTS, user-visible currentTime starts at 0)
- Live streams with non-zero PTS: encoder PTS counts from broadcast
  start; sliding-window updates roll segments off the start

All three share the same mechanism: detect first-segment baseMedia-
DecodeTime from MP4 tfdt box, compute presentationTimeOffset, apply
via SourceBuffer.timestampOffset before initial segment append.
currentTime and seekable range are offset-corrected naturally.

Combines Notion epic #6 ("Non-zero PTS / Instant Clip Support")
scope per the prior session-level absorption of instant-clip-support
into non-zero-pts-support. Carves out discontinuity-handling as a
new sister candidate (mid-stream PTS jumps via EXT-X-DISCONTINUITY,
encoder restart, 33-bit PTS rollover).

Cross-cutting impact captures: PTS-detection location (parser vs
MSE-side), timestampOffset write timing in mse-mms-pipeline's
setup sequence, per-type offset (video vs audio), seekable-range
semantics composition with live-stream-support's setLiveSeekableRange,
live + encoder-restart boundary with the carved-out sister feature,
init-segment vs first-media-segment PTS detection.

Open questions left explicit: parser depth, EXT-X-PROGRAM-DATE-TIME
consumption, EXT-X-START parsing, per-type offset, discontinuity
boundary, telemetry exposure, currentTime-near-zero browser quirks,
composition with capability-probing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- live-stream-support.md: drop brackets on [non-zero-pts-support]
  in 2 sites (Out of scope, Related features); update wording to
  reflect non-zero-pts-support is now the documented cluster B
  foundation rather than a candidate.
- dvr-event-stream-support.md: drop brackets in Related features.
- ll-hls-support.md: drop brackets in Related features.
- clusters.md: cluster B description gains Docs line (non-zero-
  pts-support); Foundational primitives note updated — primitive
  lives in non-zero-pts-support, consumed by every cluster A
  feature. Sub-cluster note for Borderline content compensation
  updated to reflect non-zero-pts-support is now the foundation
  (not foundation candidate).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…simulated)

User feedback: the doc pre-decided SourceBuffer.timestampOffset as
THE mechanism for applying the time offset. timestampOffset has
known A/V sync risks (per-SourceBuffer write means video and audio
buffers have separate offset values; sub-millisecond detection-time
precision differences can cause A/V drift that compounds; mid-source
offset changes require coordinated multi-buffer writes; browser
quirks especially on Safari). The alternative — simulate the offset
via adapter + math in behaviors/compositions — keeps the MSE buffer
"pure" with original PTS and translates at consumer boundaries
(translation surface trade-off but no A/V drift risk).

Changes:
- Opening paragraph: mechanism is now explicitly flagged as an open
  question; both approaches summarized.
- Phase rows: Notes mechanism-neutral ("apply via the chosen
  mechanism") rather than baking in timestampOffset.
- Cross-cutting impact: renamed "timestampOffset write timing" bullet
  to "Mechanism choice: timestampOffset vs simulated translation
  (load-bearing)". Walks through (a) timestampOffset trade-offs and
  (b) simulated-translation trade-offs. Notes (b) is more SPF-natural
  (behavior-driven translation fits the composition model); (a) is
  more browser-API-natural.
- "currentTime semantics" bullet: rewritten as depends-on-mechanism.
- Open questions: new entry "Mechanism choice" capturing the
  load-bearing decision; no lean yet (needs empirical A/V sync
  testing under (a) + scoping translation surface under (b)).
- Related features (mse-mms-pipeline): depends-on-mechanism note.
- See also: timestampOffset reference annotated as "one of the two
  candidate mechanisms."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the first cluster B Borderline content-compensation feature doc.
Heuristically detect "pseudo-ended" state — playback stalls near
duration boundary on sources with mismatched manifest-duration vs
actual segment data (canonically Safari) — and trigger termination
cleanly so ended fires correctly.

Two phases (detection + action), Naive vs Full depth per row matching
Notion epic #10's framing:
- Detection: Naive = don't (status quo). Full = heuristic monitor of
  playhead approaching duration + non-progressing + buffer doesn't
  reach duration.
- Action: Naive = passive (browser stalls). Full = trigger clean
  termination via MediaSource.endOfStream() OR mediaSource.duration
  adjustment (mechanism choice open).

Composition: VOD-engine variant only. Live engine variants don't
carry this behavior (live has Infinity duration, no boundary to
approach). Per the failure-mode catalog's composition-variant entry —
variant-specific behavior, not runtime branch.

Cross-cutting impact captures: action-mechanism choice trade-offs
(endOfStream caller coordination with mse-mms-pipeline's existing
gate vs duration multi-writer), distinction from buffer-stall-recovery
(both detect "not progressing"; discriminator is "near duration
boundary"), composition-variant placement, threshold tuning,
false-positive avoidance, browser-specificity.

Open questions: action-mechanism choice, detection thresholds,
coordination with buffer-stall-recovery, live→VOD transition shape,
browser detection gate, composition with edit-list-compensation,
duration-undefined defensive case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
clusters.md cluster B sub-cluster note updated: pseudo-ended-detection
moves from "candidate" to "Docs" with brief description. Edit-list-
compensation and buffer-stall-recovery remain as "remaining candidates"
(both about to land in this session). The earlier "Whether the three
land as separate feature docs or a single umbrella doc is open" line
removed — decision was made (separate features).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the second cluster B Borderline content-compensation feature.
Parse MP4 init-segment edit-list (elst) boxes; extract implied
presentation-time offsets; compensate engine-side for the shift. The
canonical "middle pattern" example per clusters.md classification:
initPTS detection -> offset state -> append behavior.

Three phases:
- Edit-list parsing (Naive: don't parse; Full: parse elst from init
  segment via shared targeted MP4 box extractor with non-zero-pts-
  support's tfdt parser)
- Offset application (Naive: rely on browser; Full: engine-side
  compensation. Shares mechanism choice with non-zero-pts-support —
  timestampOffset vs simulated translation)
- Multi-entry edit lists (Naive default: single-entry only; Full:
  multi-entry with loops/freeze-frames/speed changes — deferred until
  customer demand)

Foundation dependency on non-zero-pts-support: shared offset-
application mechanism question must resolve jointly. Composition of
the two offsets (non-zero-PTS + edit-list) is the load-bearing
open question; lean additive with debug exposure.

Cross-cutting impact captures: offset composition shape, shared
mechanism with non-zero-pts-support, browser-variance gate (Safari
honors elst; Chrome/Firefox don't always — risk of double-shift),
elst parser shape (shared with tfdt), per-track edit lists (video vs
audio independent), mid-source edit-list changes (discontinuity
territory), backend-fix exit condition (Notion epic flagged).

Open questions left explicit: offset composition, joint mechanism
choice, browser-variance gate, multi-entry support trigger, per-
track semantics, elst parser depth, backend-fix exit condition, Mux
Video relevance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- clusters.md cluster B sub-cluster note: edit-list-compensation
  added to Docs; only buffer-stall-recovery remains as candidate.
- pseudo-ended-detection.md: drop brackets on [edit-list-compensation]
  Related features entry.
- non-zero-pts-support.md: drop brackets on edit-list-compensation in
  the Borderline sister cross-ref list (alongside pseudo-ended-
  detection which already had brackets dropped).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the third and final cluster B Borderline content-compensation
feature, completing the sub-cluster trio. Detect mid-stream playback
stalls (playhead non-progressing despite buffer not at end, source
not pseudo-ended, no in-flight retry) and trigger recovery actions
in escalation order: seek-nudge -> buffer flush + refetch -> source
reset.

Two phases (detection + recovery action) with Naive/Full per row:
- Detection: Naive = don't (status quo, browser handles or hangs).
  Full = heuristic monitor with stall threshold + coordination
  gates with pseudo-ended-detection (composition order) and
  network-resilience (don't fire during in-flight retry).
- Recovery action: Naive = passive. Full = three-action escalation
  with per-step success-detection thresholds. Lightest first
  (seek-nudge -> flush+refetch -> source-reset).

Composition: always-on (applies to both VOD and live). Unlike
pseudo-ended-detection (VOD variant only), buffer-stall-recovery
applies uniformly; live just triggers it more often.

Cross-cutting impact captures: coordination with pseudo-ended-
detection (mutually-exclusive triggers via near-boundary
discriminator), coordination with network-resilience (gate on
"no retry in flight"), recovery escalation order + thresholds,
recovery-action implementation surfaces (mse-mms-pipeline,
buffer-management, source-replacement-like reset), false-positive
avoidance (paused / seeking / initial-load / background tabs),
live vs VOD threshold differences, multi-signal-abr visibility
coordination.

Open questions left explicit: escalation order, detection
threshold, pseudo-ended coordination shape, network-resilience
gate slot, source-reset implementation, customer-policy hooks,
page visibility coordination, backend exit condition, Notion-
flagged deterministic testing challenge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes the cluster B Borderline content-compensation sub-cluster
trio (pseudo-ended + edit-list + buffer-stall).

- clusters.md: cluster B sub-cluster note adds buffer-stall-recovery
  to Docs; no remaining candidates in the sub-cluster.
- pseudo-ended-detection.md: drop brackets on [buffer-stall-recovery]
  Related features entry.
- edit-list-compensation.md: drop brackets on [buffer-stall-recovery]
  Related features entry.
- non-zero-pts-support.md: drop brackets on buffer-stall-recovery in
  the Borderline sister cross-ref list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n-scope)

Adds container-support as a standalone (cluster-less) coarse feature
doc. Documents the strategic posture: Mux Video uses fMP4 across its
delivery infrastructure; legacy MPEG-TS support is not on the roadmap
for the foreseeable future. Notion epic #5 explicitly flags this as
"Possibly an RFC, not an epic."

Standalone cluster placement (not cluster D) because MSE doesn't
accept non-fMP4 containers per spec — the concern is structurally
different from capability-probing's "what can the browser decode
within MSE-accepted containers?" framing.

Two brief phases:
- Container-format detection (signal-driven; HLS CODECS attribute +
  segment magic-byte sniffing; small parser extension)
- Transmuxer integration (the major code footprint; reference
  implementations mux.js / hls.js; ~10kloc range)

Cross-cutting impact captures: transmuxer-as-dependency-vs-inline,
detection-vs-probing distinction, composition-variant placement,
codec data routing (PMT vs stsd), bandwidth-sampling adjustment,
performance considerations for live + MPEG-TS, MPEG-TS edge cases.

Open questions: revisit conditions (when does fMP4-only posture
change?), container-detection-without-transmuxer as a lower-cost
intermediate option, transmuxer dependency choice if built, HLS
mixed-container support, RFC route per Notion guidance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- capability-probing.md: resolve the "Container-detection scope" open
  question — container-support is documented as standalone
  (cluster-less; MSE doesn't accept non-fMP4 containers per spec).
  Container-detection-without-transmuxer remains a possible cross-
  feature integration point.
- Related features entry: drop brackets on [container-support];
  expanded note clarifies the cluster-D boundary distinction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds audio-only-composition as a coarse Media-src feature doc.
Engine support for HLS sources containing only audio renditions
(no video tracks declared in manifest). Notion's "Composition cases
per mode" Case 1: default composition handles a manifest that is
genuinely audio-only.

Status: partially supported today. The HLS engine tolerates audio-
only sources per engine.test.ts "handles audio-only stream" test.
Tolerance derives from video-side behaviors no-op-ing when
presentation.videoTracks is empty. This feature makes that tolerance
explicit via composition-variant subtraction.

Three phases: audio-only manifest recognition (works today),
audio-only engine variant (explicit composition-variant subtracting
video-side behaviors), audio-only-optimized buffer/playback
(Tier-2 optimizations: shorter buffer targets, no display work).

Out of scope: audio-only mode override (Case-2 Player feature; the
"use case composition" doc-type not yet formalized) which produces
audio-only delivery even from mixed-manifest sources. Falls under
parallel concepts like background-video / audio-podcast mode.

Cross-cutting impact captures: engine composition shape for variants
(matches live vs VoD precedent), behavior composition subtraction,
variant-decision signal source (shared question across all variant
features), endOfStream gate composition (variant-agnostic),
audio-only ABR pairing with audio-abr, DOM exposure semantics.

Open questions: engine variant factory shape, variant-decision
signal source, audio-only buffer-target tuning, coordination with
audio-abr, live + audio-only intersection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds video-only-composition as a coarse Media-src feature doc. Mirror
of audio-only-composition on the inverse axis. Engine support for HLS
sources containing only video renditions (no audio tracks). Notion
NEW-A: "Default composition handles a manifest that is genuinely
video-only."

Status: not explicitly implemented. Engine tolerance for missing
audio is less established than for missing video — empirical
verification needed (does setupAudioBufferActors / loadAudioSegments
fail / no-op consistently when audioTracks is empty?). Test fixture
work is a prerequisite for scoping implementation.

Three phases mirror audio-only-composition: manifest recognition,
engine variant (subtractive composition of audio-side behaviors),
optimized buffer / playback (muted-playback assumption, loop
integration, no Firefox mozHasAudio quirk consideration).

Mux relevance: mux-background-video (GitHub #873) is the canonical
prior-art consumer. Cross-link for product context — the engine-
support baseline (this feature) intersects with the background-video
product use case (Case-2 use-case-composition concern). Boundary:
this feature handles "source is video-only"; use-case-composition
handles "deliver video-only from any source."

Out of scope: video-only mode override (Case-2 Player feature, "use
case composition" type) — Notion epic NEW-B, cross-referenced with
mux-background-video #873's placement question.

Cross-cutting impact captures: engine composition shape (matches
audio-only-composition's pattern; resolves jointly), Firefox
mozHasAudio quirk verification, background-video product intersection,
autoplay-muted considerations, loop behavior coordination with
buffer-management.

Open questions: empirical video-only tolerance verification, engine
variant factory shape (shared question), variant-decision signal
source, loop integration scope, Firefox + video-only verification,
mux-background-video placement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…oss-refs

audio-playback.md: drop brackets on [audio-only-composition] in three
sites (What's not implemented, open question, Related features).
All now plain-link audio-only-composition.md.

No corresponding cross-refs to update for video-only-composition
(the candidate wasn't bracketed in any existing feature doc beyond
clusters.md's Composition cases per mode table, which is already
descriptive and doesn't need updating).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cjpillsbury cjpillsbury changed the title docs(spf): spf feature docs and skills docs(spf): seed feature registry, clusters reference, and document-feature skill May 21, 2026
@cjpillsbury cjpillsbury marked this pull request as ready for review May 21, 2026 13:25
Copy link
Copy Markdown
Collaborator

@luwes luwes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@cjpillsbury cjpillsbury force-pushed the docs/spf-feature-docs-and-skills branch from ca2f700 to e1eeebb Compare May 21, 2026 17:55
@cjpillsbury cjpillsbury merged commit c9d6cce into main May 21, 2026
26 checks passed
@cjpillsbury cjpillsbury deleted the docs/spf-feature-docs-and-skills branch May 21, 2026 19:07
@cursor cursor Bot mentioned this pull request May 25, 2026
luwes pushed a commit that referenced this pull request May 27, 2026
…ature skill (#1581)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants