--- status: draft date: 2026-05-20 --- # Feature Clusters & Cross-Cluster Patterns Heuristics for grouping SPF features by the areas of state and behavior they touch. Used by the `extend-feature` skill (and, later, the intake/scoping skill) to identify related features, point the skill at the right reference material, and surface cross-cutting impacts when a new feature lands. ## How the skill uses this doc When a feature is being documented or extended, two questions recur: 1. **What other features does this likely interact with?** Cluster membership is the first-order answer. 2. **What cross-cutting concerns does this likely involve?** Cross-cluster patterns are the second-order answer. The skill consults this doc at multiple steps: - **Source gathering** — trigger signals identify likely clusters from the user's invocation, expanding the related-context net. - **Code grounding** — clusters point Explore at the right files. - **Cross-cutting concern checks** — clusters and patterns drive which checks fire (e.g., buffer-touching → MSE codec-change check; new state-slot writer → multi-writer characterization). - **Relationships** — clusters' existing + anticipated docs become the seed set for `Related features`. - **Cross-doc cascade** — clusters identify which other docs are candidates for narrow update once the new feature lands. ## How this doc grows Both clusters and patterns are extract-from-real-work — no speculative additions. Triggers for updating: - A new feature surfaces a signal not covered → extend an existing cluster or file a new one. - A pattern recurs across two or more clusters → add to Cross-cluster patterns. - A new feature gets documented → add it to the relevant cluster's Docs list. ## Notation Existing features cite their file: `subtitles`, `video-abr`, `multi-language-audio`. Notion-cluster cross-references (e.g., "Notion cluster C") point at the SPF Epics Working Doc taxonomy. Unscoped feature candidates that previously appeared inline as bracketed names (e.g. `[ll-hls-support]`) have been pulled out — they're tracked separately as an unscoped backlog rather than as sibling-feature placeholders in cluster Docs lists. Documenting any of them routes through `/spf-document-feature`, which applies the decomposition rubric at invocation time and decides whether the item becomes a new doc, a phase of an existing doc, or absorption into another doc. --- ## Feature classification axes Orthogonal to cluster membership, features can be classified along axes that surface during scoping. These distinctions are extracted from the SPF Epics Working Document (Notion) and recur during planning and doc work. They're useful both for new feature docs (knowing what category an item is) and for existing ones (locating where the feature sits on each axis when surfacing what's not implemented). ### Media-src vs Player vs Borderline The primary cut for unimplemented work. | Category | Definition | Examples | |---|---|---| | **Media-src feature** | Required to support a media-src permutation. Without it, the source either doesn't play or doesn't play correctly. "Correctness" means the engine handles the *presence* of the permutation type — playing one audio track of a multi-language source isn't supporting multi-language audio | `live-stream-support`, `multi-language-audio` | | **Player feature** | Additive functionality not tied to making any source work. Player chooses to do it | *(no documented examples yet — shapes include billing- / viewport-driven selection caps and composition-mode features)* | | **Borderline** | Accounts for technically valid but suboptimally-formed-or-delivered content (*content compensation*) or for response errors that emerge from playback behavior (*response-error handling*). The source plays; the work makes it play better in specific quirky cases | *(no documented examples yet — shapes include content-compensation for suboptimal sources and response-error handling)* | ### Naive vs Full implementation depth Within a single feature; both depths are valid implementations. Used to scope inside a feature doc, not to split features. | Example | Naive | Full | |---|---|---| | Termination detection (in `live-stream-support`) | `#EXT-X-ENDLIST` recognition only | ENDLIST + unchanged-playlist miss-counter fallback | | Response error handling | Generic 4xx retry/backoff (≈ what hls.js does today) | Response-aware: detect specific signatures, adjust pacing | | Stalled-playback detection | Don't detect — accept the stall | Heuristic detection of pseudo-ended state; fire `ended` correctly | | Token expiry | Treat 4xx as fatal (≈ hls.js) | Provider-aware refresh / recovery hooks | A feature doc may describe phases that span depth: a partial implementation at one depth still counts as work toward the feature. ### "Can play" vs actual support For some features, the difference between "source plays" and "feature supported" is *not* a matter of implementation depth — it's the difference between unrelated correctness happening to hold and the feature actually being supported. | Example | "Can play" (not partial credit) | Actual support | |---|---|---| | `multi-language-audio` | Source plays via the default audio track | Tracks recognized, exposed via API, selectable | | `subtitles` (already supported) | Source plays without subtitles displaying | Tracks parsed, exposed, displayable | The "can play" state does *not* count as partial credit toward the feature. ### Tier 1 (spec-compliant baseline) vs Tier 2 (custom behavior) Especially applicable within the selection cluster but useful as a general lens. Tier 1 is generally a prerequisite for Tier 2 and easier to verify. | Item | Tier 1: Spec-compliant | Tier 2: Custom behavior | |---|---|---| | `multi-language-audio` | Recognize tracks; honor `DEFAULT` / `AUTOSELECT` | Programmatic select + persistence API | ### Composition vs Policy vs middle pattern A feature's *implementation shape* falls along a spectrum: | Mechanism | Definition | Where it lives | Examples | |---|---|---|---| | **Policy** | Pure config / function variation consumed by an existing behavior. No new behaviors | Inside an existing behavior | *(no documented examples yet — shape: a height cap consumed by a `selectQuality` config)* | | **Middle pattern** | A new state-producing behavior monitors an external signal and updates state; existing consumer behaviors update to respect that state. Heavier than pure policy but lighter than composition | New behavior + targeted edits to consumers | *(no documented examples yet — shapes: `ResizeObserver` → cap state → switching; CDN-tracking → selection state → failover; buffer-state monitor → ended signal; `initPTS` detection → offset state → append)* | | **Composition** | A different composed engine. Alternative compositions assemble a variant via one or more composition mechanisms (subtract / add / alternative-implementation / alternative-default-configuration), in any combination, to handle different *modes* or *delivery scenarios*. See [`use-cases/README.md`](../use-cases/README.md) for the full doc-type, mechanism taxonomy, and decomposition rubric | At the Adapter level, on initial conditions | *(no instance docs yet; candidates in [`use-cases/README.md`](../use-cases/README.md) Index — `audio-only-mode-override`, `video-only-mode-override`, `background-looping-video`)* | Composition is bounded to **modes** and **delivery scenarios** — see [`use-cases/README.md`](../use-cases/README.md) for the doc-type that captures composition variants. Most "feels like composition" items actually fit the middle pattern. --- ## Clusters ### Engine lifecycle Engine instantiation, source loading lifecycle, and per-source identity resets. The cross-cutting concerns that bracket everything else — preload semantics gate when work begins, source resolution drives the resolved/unresolved cascade that every downstream behavior rides. **Signals.** `state.presentation` lifecycle (unresolved `{ url }` ↔ resolved `Presentation`); `state.preload` + `state.loadActivated` as the loading gate; `resolvePresentation`'s 4-state FSM (`'preconditions-unmet' → 'idle' → 'resolving' → 'resolved'`); `isResolvedPresentation` predicate; "ride resolver's resolved/unresolved lifecycle" pattern; `AbortController`-bound-to-state-exit cleanup; per-source-identity resets (`loadActivated`, `selected*TrackId`); cross-source preservation (`bandwidthState` for ABR resume). **Docs.** `preload-modes`, `source-replacement`, `engine-adapter-integration`. **Foundational primitives.** `state.presentation` as the source-identity slot; resolved/unresolved routing in `resolvePresentation` as the cleanup-cascade driver; the per-presentation-gated behavior cleanup contract (state-exit detaches DOM / destroys actors / aborts in-flight fetches); `shareSignals` for the external write surface. **Common cross-cluster touchpoints.** Every other cluster. Track & variant registry, MSE / Buffer management, and Presentation modeling all gate on resolved presentation; their setup behaviors tear down via the resolved/unresolved cascade. Time normalization survives across resets (`currentTime` DOM-side mirror via `trackCurrentTime`). Manifest reload loop is presentation re-resolution under live conditions — a special case of the same cascade. **Key check.** New behaviors that gate on `isResolvedPresentation` MUST honor the state-exit cleanup contract: detach DOM resources / destroy actors / clear context slots / abort in-flight fetches on state exit. The in-place source-replacement validation test (`engine.test.ts` → "cleanly replaces source in place via state.presentation overwrite") pins this contract against regression. --- ### Track & variant registry The selection model — which audio / video / text tracks are available, which is active, who can change the selection. **Signals.** Track selection, switching, filtering, sorting; per-type tracks; state slots named `selected{Audio,Video,Text}TrackId`; behaviors named `select*Track`, `switch*Quality`, `sync*Tracks`; multi-writer track-id slots; manifest renditions with `LANGUAGE` / `NAME` / `DEFAULT` / `AUTOSELECT` / `FORCED` / `CHANNELS` attributes; `userVideoTrackSelection` and similar constraint slots. **Docs.** `subtitles`, `video-abr`, `audio-playback`, `multi-language-audio`, `hevc-variant-selection` (cluster D consumer, video codec axis), `5.1-surround-selection` (cluster D consumer, audio channel-count axis), `audio-abr` (audio sibling of video-abr), `multi-signal-abr` (algorithm extension to ABR with non-bandwidth signals). **Foundational primitives.** Per-track resolution (`resolveVideoTrack` / `resolveAudioTrack` / `resolveTextTrack` sharing `setupTrackResolution`); per-track selection (`selectVideoTrack` / `selectAudioTrack` / `selectTextTrack`); the `selected*TrackId` slot family. **Maps to Notion cluster C** ("Track & variant registry"). **Common cross-cluster touchpoints.** Multi-writer state slots (text + proposed audio); constraint + filter (video-abr's `userVideoTrackSelection`); per-type specialization (resolve / load / setup are per-type today). --- ### Selection policy Modes, caps, and overrides layered on top of the registry. Where the engine's selection logic is constrained or biased by something other than bandwidth or default-selection logic. **Signals.** Quality caps (max-height, max-bitrate, max-FPS, screen-size); modes (audio-only, video-only); user overrides via constraint slots; "respect billing constraints" / "respect device capabilities" framing; viewport adaptation. **Docs.** `rendition-selection-caps`. `video-abr`'s `userVideoTrackSelection` is the constraint+filter precedent this cluster's caps build on. **Foundational primitives.** Constraint slots that filter the candidate set before selection runs. **Maps to Notion cluster E** ("Selection policy layer"). **Common cross-cluster touchpoints.** Capability probing (caps gate on device support); track & variant registry (policy filters the registry's candidate set); composition variants (audio-only is a subtract-down composition). --- ### Presentation modeling Fetching, parsing, and modeling HLS / HAS media — the data structures the rest of the engine consumes. **Signals.** Manifest fetching, multivariant playlist parsing, media playlist parsing, presentation modeling; `parseMultivariantPlaylist`, `parseMediaPlaylist`; state slot `presentation` (resolved vs unresolved); `presentation-resolved` state-machine transitions; `PartiallyResolvedTextTrack`-style modeling shapes; HLS attribute extraction. **Docs.** None yet. The architectural deep-dive [`presentation-modeling.md`](../presentation-modeling.md) covers the format-neutral data shape and per-track resolution layer that feature docs in this cluster would consume. **Foundational primitives.** `Presentation` data shape; `resolvePresentation` behavior + the per-track `resolve*Track` family that patches resolved tracks back into `presentation`. **Common cross-cluster touchpoints.** Track & variant registry (parser surfaces tracks the registry exposes); manifest reload loop (live presentations re-resolve over time); capability probing (parsed CODECS used for capability filtering). --- ### MSE / Buffer management `MediaSource` lifecycle, `SourceBuffer` setup, append, flush, and end-of-stream coordination. The boundary between SPF and the browser's media pipeline. **Signals.** `setupMediaSource`, `setupVideoBufferActors`, `setupAudioBufferActors`, `updateMediaSourceDuration`, `endOfStream`, `loadVideoSegments`, `loadAudioSegments`; SourceBuffer setup / append / remove / `changeType()`; per-type buffer setup; `SourceBufferActor` and `SegmentLoaderActor`; ManagedMediaSource (MMS) vs MediaSource (MSE) distinction; codec-bound mimeType ("video/mp4; codecs=..."); A+V SourceBuffer separation per CMAF; forward-buffer / back-buffer management; buffer flush mechanics. **Docs.** `mse-mms-pipeline`, `buffer-management`. `subtitles`'s `loadTextTrackSegments` runs the same preload-aware loading FSM but does not touch MSE buffers. `video-abr`'s same-SourceBuffer-different-bitrate-segments pattern is the precedent for in-track switching. **Foundational primitives.** Per-type `SourceBufferActor` + `SegmentLoaderActor` pair; `createTrackedFetch` (segment fetch with bandwidth sampling baked in); the preload-aware load FSM (`'preconditions-unmet' → 'dormant' → 'metadata-only' → 'full-range'`). **Common cross-cluster touchpoints.** Track & variant registry (selection drives which buffer gets fed); gating (preload + DRM both gate buffer setup and append); time normalization (segment-boundary crossing drives load timing); per-type specialization (one buffer per type, paired actors). **Key check.** For any feature that touches buffer behavior, identify whether the codec changes. Same codec → flush + replan, no setup re-entry. Codec change → `changeType()` or buffer recreation, routes to a codec-change feature (5.1 surround, HEVC). --- ### Time normalization The mapping between media timeline, playlist position, and `