# Structured mpegts.js Diagnostics Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Replace mpegts.js message heuristics with a version-locked, allowlisted evidence boundary shared by HTML5, Video.js, and ArtPlayer. **Architecture:** Normalize only mpegts.js 1.8.0 public error type/detail constants and the exact HTTP-status slot into `MpegTsPlaybackEvidence`. The shared classifier maps internally consistent evidence to existing diagnostic codes, and the existing viewport renders only structured evidence while the three engine owners keep their current source and lifecycle behavior. **Tech Stack:** Angular 21, TypeScript 5.9, mpegts.js 1.8.0, Jest through Nx, Markdown architecture and release-note documentation. --- ### Task 0: Confirm The Isolated Baseline And Public Runtime **Files:** - Verify: `package.json` - Verify: `pnpm-lock.yaml` - Verify: `node_modules/mpegts.js/src/player/player-errors.js` - Verify: `node_modules/mpegts.js/src/io/loader.js` - Verify: `node_modules/mpegts.js/src/core/transmuxing-controller.js` - Verify: `node_modules/mpegts.js/d.ts/mpegts.d.ts` - [x] **Step 1: Create the isolated feature branch from current master** Run: ```bash git switch -c agent/structure-mpegts-diagnostics origin/master ``` Expected: branch starts at `9f4e11d6d`, the merge of PR #1318. - [x] **Step 2: Verify dependency and Nx availability** Run: ```bash test -d node_modules pnpm nx show projects --withTarget test ``` Expected: exit 0 and output containing `ui-playback`. - [x] **Step 3: Run the affected-project baseline** Run: ```bash pnpm nx test ui-playback ``` Expected: 87 suites and 849 tests pass before implementation. - [x] **Step 4: Audit the installed public contract** Confirm: ```text mpegts.js version = 1.8.0 ErrorTypes = NetworkError, MediaError, OtherError ErrorDetails = Exception, HttpStatusCodeInvalid, ConnectingTimeout, UnrecoverableEarlyEof, MediaMSEError, FormatError, FormatUnsupported, CodecUnsupported HttpStatusCodeInvalid info = { code: HTTP status, msg: status text } recoverable EarlyEof is handled internally before the public player error ``` ### Task 1: Drive The Public Evidence Boundary From Failing Tests **Files:** - Create: `libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.model.ts` - Create: `libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.ts` - Create: `libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts` - [x] **Step 1: Write the failing installed-runtime contract test** Create `mpegts-playback-evidence.util.spec.ts` and assert the real installed runtime before using any mocks: ```typescript import mpegts from 'mpegts.js'; import { MpegTsPlaybackEngineDetails, MpegTsPlaybackEngineType, } from './mpegts-playback-evidence.model'; import { MPEGTS_DIAGNOSTIC_VERSION, createMpegTsPlaybackEvidence, } from './mpegts-playback-evidence.util'; describe('mpegts.js playback evidence', () => { it('locks the accepted public contract to mpegts.js 1.8.0', () => { expect(mpegts.version).toBe(MPEGTS_DIAGNOSTIC_VERSION); expect(mpegts.ErrorTypes).toEqual({ NETWORK_ERROR: MpegTsPlaybackEngineType.Network, MEDIA_ERROR: MpegTsPlaybackEngineType.Media, OTHER_ERROR: MpegTsPlaybackEngineType.Other, }); expect(mpegts.ErrorDetails).toEqual({ NETWORK_EXCEPTION: MpegTsPlaybackEngineDetails.NetworkException, NETWORK_STATUS_CODE_INVALID: MpegTsPlaybackEngineDetails.HttpStatusCodeInvalid, NETWORK_TIMEOUT: MpegTsPlaybackEngineDetails.ConnectingTimeout, NETWORK_UNRECOVERABLE_EARLY_EOF: MpegTsPlaybackEngineDetails.UnrecoverableEarlyEof, MEDIA_MSE_ERROR: MpegTsPlaybackEngineDetails.MediaMseError, MEDIA_FORMAT_ERROR: MpegTsPlaybackEngineDetails.FormatError, MEDIA_FORMAT_UNSUPPORTED: MpegTsPlaybackEngineDetails.FormatUnsupported, MEDIA_CODEC_UNSUPPORTED: MpegTsPlaybackEngineDetails.CodecUnsupported, }); }); }); ``` Run: ```bash NODE_OPTIONS=--experimental-vm-modules node node_modules/jest/bin/jest.js \ --config jest.web-esm.workspace.ts --runTestsByPath \ libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts \ --runInBand ``` Expected: FAIL because the model and evidence modules do not exist. - [x] **Step 2: Add the focused evidence model** Create `mpegts-playback-evidence.model.ts` with const-derived unions: ```typescript export const MpegTsPlaybackEngineType = { Network: 'NetworkError', Media: 'MediaError', Other: 'OtherError', Unknown: 'unknown', } as const; export type MpegTsPlaybackEngineType = (typeof MpegTsPlaybackEngineType)[keyof typeof MpegTsPlaybackEngineType]; export const MpegTsPlaybackEngineDetails = { NetworkException: 'Exception', HttpStatusCodeInvalid: 'HttpStatusCodeInvalid', ConnectingTimeout: 'ConnectingTimeout', UnrecoverableEarlyEof: 'UnrecoverableEarlyEof', MediaMseError: 'MediaMSEError', FormatError: 'FormatError', FormatUnsupported: 'FormatUnsupported', CodecUnsupported: 'CodecUnsupported', Unknown: 'unknown', } as const; export type MpegTsPlaybackEngineDetails = (typeof MpegTsPlaybackEngineDetails)[keyof typeof MpegTsPlaybackEngineDetails]; export const MpegTsPlaybackDisposition = { Terminal: 'terminal' } as const; export type MpegTsPlaybackDisposition = (typeof MpegTsPlaybackDisposition)[keyof typeof MpegTsPlaybackDisposition]; export const MpegTsPlaybackStage = { Loader: 'loader', Demux: 'demux', MediaSource: 'media-source', Unknown: 'unknown', } as const; export type MpegTsPlaybackStage = (typeof MpegTsPlaybackStage)[keyof typeof MpegTsPlaybackStage]; export const MpegTsPlaybackFailure = { Http: 'http', Timeout: 'timeout', Network: 'network', TruncatedStream: 'truncated-stream', Format: 'format', Codec: 'codec', MediaSource: 'media-source', Unknown: 'unknown', } as const; export type MpegTsPlaybackFailure = (typeof MpegTsPlaybackFailure)[keyof typeof MpegTsPlaybackFailure]; export interface MpegTsPlaybackEvidence { readonly engineType: MpegTsPlaybackEngineType; readonly engineDetails: MpegTsPlaybackEngineDetails; readonly disposition: MpegTsPlaybackDisposition; readonly stage: MpegTsPlaybackStage; readonly failure: MpegTsPlaybackFailure; readonly httpStatus?: number; } ``` Use const-derived aliases for disposition, stage, and failure rather than duplicating string unions if TypeScript reports drift. - [x] **Step 3: Add failing sanitization and mapping cases** Extend the spec with a table covering every consistent pair and expected stage/failure. Add explicit cases proving: ```typescript expect( createMpegTsPlaybackEvidence('NetworkError', 'HttpStatusCodeInvalid', { code: 404, msg: 'Not Found secret', url: 'https://provider.example/live.ts?token=secret', }) ).toEqual({ engineType: 'NetworkError', engineDetails: 'HttpStatusCodeInvalid', disposition: 'terminal', stage: 'loader', failure: 'http', httpStatus: 404, }); expect( createMpegTsPlaybackEvidence('OtherError', 'CodecUnsupported', { code: 503, }) ).toEqual({ engineType: 'OtherError', engineDetails: 'CodecUnsupported', disposition: 'terminal', stage: 'unknown', failure: 'unknown', }); ``` Also assert status strings, 399, 600, nested `code`, lowercase constants, unknown objects, and circular payloads do not add `httpStatus` or throw. Run the focused spec. Expected: FAIL because the factory is missing. - [x] **Step 4: Implement the minimal sanitizer** Create `mpegts-playback-evidence.util.ts` with: ```typescript export const MPEGTS_DIAGNOSTIC_VERSION = '1.8.0'; export function createMpegTsPlaybackEvidence( type: unknown, details: unknown, info: unknown ): MpegTsPlaybackEvidence { const engineType = normalizeEngineType(type); const engineDetails = normalizeEngineDetails(details); const { stage, failure } = deriveMpegTsStageAndFailure( engineType, engineDetails ); const httpStatus = engineType === MpegTsPlaybackEngineType.Network && engineDetails === MpegTsPlaybackEngineDetails.HttpStatusCodeInvalid ? readHttpStatus(info) : undefined; return { engineType, engineDetails, disposition: MpegTsPlaybackDisposition.Terminal, stage, failure, ...(httpStatus === undefined ? {} : { httpStatus }), }; } ``` Implement exact `switch` statements for type/detail normalization and the eight accepted pairs. `readHttpStatus` reads only top-level `code` and accepts integers 400–599. Do not stringify, clone, enumerate, or inspect any message or unknown property. - [x] **Step 5: Run the evidence spec green** Run the focused Jest command from Step 1. Expected: PASS with the runtime contract and sanitizer cases green. - [x] **Step 6: Commit the evidence boundary** ```bash git add -- \ libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.model.ts \ libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.ts \ libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts git commit -m "fix(playback): sanitize mpegts error evidence" ``` ### Task 2: Replace Heuristic Classification With Structured Evidence **Files:** - Modify: `libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.model.ts` - Modify: `libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts` - Modify: `libs/ui/playback/src/lib/playback-diagnostics/playback-error-patterns.util.ts` - Modify: `libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts` - Test: `libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts` - [x] **Step 1: Write failing classifier tests** Add table-driven expectations that pass evidence, not raw errors: ```typescript const issue = classifyMpegTsPlaybackIssue( createMpegTsPlaybackEvidence( 'NetworkError', 'HttpStatusCodeInvalid', { code: 404, msg: 'secret' } ), createPlaybackSourceMetadata({ url: 'https://example.test/missing.ts', mimeType: 'video/mp2t', player: InlinePlaybackPlayer.VideoJs, }) ); expect(issue).toEqual( expect.objectContaining({ code: PlaybackDiagnosticCode.NetworkError, source: PlaybackDiagnosticSource.MpegTs, httpStatus: 404, mpegTs: expect.objectContaining({ failure: 'http' }), externalFallbackRecommended: false, }) ); expect(issue.details).toBeUndefined(); ``` Cover timeout/exception → network, early EOF/format/MSE → media decode, unsupported format → container, codec → codec, and inconsistent/other/unknown → unknown. Verify only decode/container/codec outcomes recommend fallback. Run the evidence and diagnostics specs. Expected: FAIL because the classifier still accepts raw `MpegTsPlaybackErrorInput` and emits `details`. - [x] **Step 2: Wire evidence into the shared diagnostic model** In `playback-diagnostics.model.ts`: ```typescript import type { MpegTsPlaybackEvidence } from './mpegts-playback-evidence.model'; export * from './mpegts-playback-evidence.model'; ``` Remove `MpegTsPlaybackErrorInput` and add: ```typescript readonly mpegTs?: MpegTsPlaybackEvidence; ``` to `PlaybackDiagnostic`. - [x] **Step 3: Implement evidence-only classification** Change the signature to: ```typescript export function classifyMpegTsPlaybackIssue( evidence: MpegTsPlaybackEvidence, metadata: PlaybackSourceMetadata ): PlaybackDiagnostic ``` Map the exact failures from the approved design, pass `httpStatus: evidence.httpStatus` and `mpegTs: evidence` to `createPlaybackDiagnostic`, and add `mpegTs` to that factory's options, destructuring, and result. Export `createMpegTsPlaybackEvidence` from the diagnostics facade. - [x] **Step 4: Remove obsolete mpegts text helpers** Delete `normalizeErrorDetails`, `isNetworkFailure`, `isEarlyEofFailure`, and their serialization helpers from `playback-error-patterns.util.ts`. Keep `isBrowserAccessFailure` for native browser errors. Remove other now-unused text helpers only when `rg` proves they have no callers. Run: ```bash rg -n "normalizeErrorDetails|isNetworkFailure|isEarlyEofFailure|MpegTsPlaybackErrorInput" \ libs/ui/playback/src/lib ``` Expected: no matches. - [x] **Step 5: Run classifier tests green** Run both focused specs by path. Expected: PASS with no raw message or arbitrary payload retained. - [x] **Step 6: Commit structured classification** ```bash git add -- \ libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.model.ts \ libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts \ libs/ui/playback/src/lib/playback-diagnostics/playback-error-patterns.util.ts \ libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts \ libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts git commit -m "fix(playback): classify exact mpegts failures" ``` ### Task 3: Route HTML5 Through The Shared Boundary **Files:** - Modify: `libs/ui/playback/src/lib/html-video-player/html-video-player-diagnostics.ts` - Modify: `libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.spec-fixtures.ts` - Modify: `libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.sources.spec.ts` - [x] **Step 1: Make the mpegts fixture emit registered events** Give `MockMpegTsPlayer` a listener map and `emit(event, ...args)` helper, matching the existing VJS and ArtPlayer test doubles. Preserve lifecycle logging for `on`, `off`, attachment, and load. - [x] **Step 2: Write the failing HTML5 HTTP regression** Start a `.ts` source, emit: ```typescript engine.emit('error', 'NetworkError', 'HttpStatusCodeInvalid', { code: 404, msg: 'Not Found html-secret', url: 'https://provider.example/error?token=html-secret', }); ``` Assert the component emits HTML5 source metadata, `network-error`, HTTP 404, structured `mpegTs` evidence, no raw `details`, and no external fallback. Run the focused sources spec. Expected: FAIL because the HTML5 helper still passes a raw error object to the classifier. - [x] **Step 3: Normalize before classification** Import `createMpegTsPlaybackEvidence` in `html-video-player-diagnostics.ts` and change: ```typescript classifyMpegTsPlaybackIssue( createMpegTsPlaybackEvidence(error.type, error.details, error.info), createHtml5SourceMetadata(url, 'video/mp2t') ) ``` Accept unknown callback values so normalization owns validation. - [x] **Step 4: Run the HTML5 regression green and commit** Run the focused sources spec, then: ```bash git add -- \ libs/ui/playback/src/lib/html-video-player/html-video-player-diagnostics.ts \ libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.spec-fixtures.ts \ libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.sources.spec.ts git commit -m "fix(playback): structure HTML5 mpegts errors" ``` ### Task 4: Route Video.js And ArtPlayer Through The Same Boundary **Files:** - Modify: `libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.ts` - Modify: `libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.spec.ts` - Modify: `libs/ui/playback/src/lib/art-player/art-player-source-session.ts` - Modify: `libs/ui/playback/src/lib/art-player/art-player-source-session.spec.ts` - Modify: `libs/ui/playback/src/lib/art-player/art-player.component.spec.ts` - [x] **Step 1: Replace the misleading VJS CORS regression** Change the existing synthetic `FetchError`/CORS case to the real public event: ```typescript mpegTsPlayer.emit( 'error', 'NetworkError', 'HttpStatusCodeInvalid', { code: 503, msg: 'provider secret', headers: { Authorization: 'secret' } } ); ``` Expect `network-error`, HTTP 503, `player=videojs`, exact mpegts evidence, no fallback, and serialized output without either secret. Add a generic `NetworkError + Exception` case proving message text that says CORS remains a generic network diagnostic. Run the VJS session spec. Expected: FAIL under the raw classifier contract. - [x] **Step 2: Normalize in VjsMpegTsSession** Import the factory and call: ```typescript classifyMpegTsPlaybackIssue( createMpegTsPlaybackEvidence(type, details, info), createPlaybackSourceMetadata({ url, mimeType: 'video/mp2t', player: InlinePlaybackPlayer.VideoJs, }) ) ``` Keep duration sync, listener cleanup, play, and teardown unchanged. - [x] **Step 3: Add the failing ArtPlayer structured regression** Emit `MediaError + CodecUnsupported` with a secret-bearing `info` object. Assert `unsupported-codec`, `player=artplayer`, structured evidence, fallback enabled, and no secret in the emitted issue. Update old lowercase/message-based fixtures to exact public constants. Run the ArtPlayer source-session and component specs. Expected: FAIL until the session uses the factory. - [x] **Step 4: Normalize in ArtPlayerSourceSession** Import `createMpegTsPlaybackEvidence` and pass the callback arguments through it before the shared classifier. Change the stored listener parameter types to `unknown` so the sanitizer, not the adapter, owns validation. Preserve the destroyed/current-engine guard. - [x] **Step 5: Run all three engine-owner specs green and commit** Run HTML5 sources, VJS session, ArtPlayer source-session, and ArtPlayer component specs by path. Then: ```bash git add -- \ libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.ts \ libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.spec.ts \ libs/ui/playback/src/lib/art-player/art-player-source-session.ts \ libs/ui/playback/src/lib/art-player/art-player-source-session.spec.ts \ libs/ui/playback/src/lib/art-player/art-player.component.spec.ts git commit -m "fix(playback): share mpegts evidence across players" ``` ### Task 5: Render Only Structured mpegts.js Details **Files:** - Modify: `libs/ui/playback/src/lib/web-player-view/web-player-view-diagnostics.utils.ts` - Modify: `libs/ui/playback/src/lib/web-player-view/web-player-view.component.spec.ts` - [x] **Step 1: Write the failing rendering and privacy regression** Add `createStructuredMpegTsDiagnostic()` with structured HTTP 404 evidence plus legacy `details` and native message fields containing sentinels. Assert: ```typescript expect(component.getDiagnosticDetails(issue)).toEqual( expect.arrayContaining([ { labelKey: 'PLAYBACK_DIAGNOSTICS.DETAIL_ERROR_DETAILS', value: 'stage=loader · failure=http · type=NetworkError · ' + 'details=HttpStatusCodeInvalid · disposition=terminal · HTTP 404', }, ]) ); expect(renderedDetails).not.toContain('mpegts-render-secret'); expect(component.getDiagnosticMeta(issue)).toBe('HTTP 404'); ``` Run the component spec. Expected: FAIL because mpegts evidence is not formatted. - [x] **Step 2: Add the structured formatter branch** Before VHS/HLS legacy formatting, add: ```typescript if (issue.mpegTs) { return [ `stage=${issue.mpegTs.stage}`, `failure=${issue.mpegTs.failure}`, `type=${issue.mpegTs.engineType}`, `details=${issue.mpegTs.engineDetails}`, `disposition=${issue.mpegTs.disposition}`, issue.mpegTs.httpStatus === undefined ? '' : `HTTP ${issue.mpegTs.httpStatus}`, ] .filter((value) => value.length > 0) .join(' · '); } ``` Also suppress the native-message row when `issue.mpegTs` exists, matching the HLS/VHS/Shaka structured boundaries. - [x] **Step 3: Run the view regression green and commit** ```bash git add -- \ libs/ui/playback/src/lib/web-player-view/web-player-view-diagnostics.utils.ts \ libs/ui/playback/src/lib/web-player-view/web-player-view.component.spec.ts git commit -m "fix(playback): render structured mpegts evidence" ``` ### Task 6: Update Canonical Documentation And Release Note **Files:** - Modify: `docs/architecture/embedded-inline-playback.md` - Modify: `AGENTS.md` - Modify: `CLAUDE.md` - Create: `.changes/playback-structured-mpegts-diagnostics.md` - [x] **Step 1: Update the canonical diagnostic contract** Replace the current early-EOF-only mpegts paragraph with the 1.8.0 public boundary: exact type/detail pairs, terminal player events, validated HTTP status, classification mapping, rejected raw fields, all three owners, and the absence of message-based browser-access guesses. - [x] **Step 2: Keep repository guidance current** Add the same concise contract to the mpegts ownership summaries in `AGENTS.md` and `CLAUDE.md`: all three owners use the shared version-locked evidence boundary; raw payloads do not cross it; shared controls remain unchanged. - [x] **Step 3: Add the user-facing release note** Create: ```markdown --- type: fix area: playback issues: [1159] --- MPEG-TS playback errors now show exact engine evidence, including HTTP status, without exposing provider response details. Format, codec, truncated-stream, and MediaSource failures now produce more accurate fallback guidance. ``` Keep the body below 400 characters. - [x] **Step 4: Validate docs and note, then commit** Run: ```bash git diff --check pnpm run release:notes:validate ``` Expected: exit 0. Then: ```bash git add -- \ docs/architecture/embedded-inline-playback.md \ AGENTS.md CLAUDE.md \ .changes/playback-structured-mpegts-diagnostics.md git commit -m "docs(playback): document structured mpegts diagnostics" ``` ### Task 7: Complete Validation And Local P1/P2 Review **Files:** - Verify all files changed since `origin/master` - [x] **Step 1: Run focused regression tests** Run all new and changed spec files by path with the web ESM Jest config. Expected: every focused suite passes. - [x] **Step 2: Run the affected validation ladder** ```bash pnpm nx test ui-playback pnpm nx lint ui-playback pnpm run typecheck:web pnpm run i18n:check pnpm run release:notes:validate ``` Expected: all commands exit 0. No new E2E is required because source routing, player lifecycle, actions, and UI layout do not change; engine events, all three adapters, classification, privacy, and rendered output have deterministic unit/integration coverage. - [x] **Step 3: Inspect size, scope, and accidental raw-data retention** ```bash git diff --check origin/master...HEAD git diff --stat origin/master...HEAD rg -n "JSON\.stringify|info\.msg|error\.message|normalizeErrorDetails|isNetworkFailure|isEarlyEofFailure" \ libs/ui/playback/src/lib/playback-diagnostics \ libs/ui/playback/src/lib/html-video-player/html-video-player-diagnostics.ts \ libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.ts \ libs/ui/playback/src/lib/art-player/art-player-source-session.ts ``` Expected: no mpegts error serialization/message inference; any unrelated matches are inspected and justified. - [x] **Step 4: Perform the requested local Codex review** Review `origin/master...HEAD` specifically for P1/P2 defects: - false classification from mismatched type/detail pairs; - unvalidated or duplicated HTTP status; - raw provider data escaping through top-level or rendered fields; - different behavior among HTML5, Video.js, and ArtPlayer; - missing stale-listener/teardown guards; - incorrect fallback behavior; - version-lock drift; - missing tests or stale canonical docs. Fix every confirmed P1/P2 finding through a new failing regression first, rerun the focused and affected validation ladders, and commit the fixes. Repeat until the review is clean. - [x] **Step 5: Verify the final branch state** ```bash git status --short --branch git log --oneline origin/master..HEAD git diff --check origin/master...HEAD ``` Expected: clean worktree, focused commits only, and no whitespace errors. ### Task 8: Publish The Pull Request After Explicit Authorization **Files:** - Inspect: all changes in `origin/master...HEAD` - [ ] **Step 1: Confirm final staged and branch scope** Do not stage unrelated files. Verify the exact branch and commits before push. - [ ] **Step 2: Push only after user authorization** ```bash git push -u origin agent/structure-mpegts-diagnostics ``` - [ ] **Step 3: Create one ready PR only after user authorization** Use base `master`, head `agent/structure-mpegts-diagnostics`, and summarize: - version-locked public mpegts.js evidence; - exact HTTP/format/codec/EOF/MSE classification; - shared HTML5, Video.js, and ArtPlayer integration; - privacy boundary and future recommendation-layer compatibility; - tests and validation run. Link issue #1159 and include the release note. Do not create multiple or draft PRs unless the user requests that state.