Files
iptvnator/docs/superpowers/plans/2026-07-27-xtream-performance-benchmark.md
T
4gray a2fafcfc08 test(performance): add end-to-end Xtream benchmark harness (#1300)
* docs(performance): plan Xtream benchmark

* feat(xtream-mock-server): add deterministic 100k fixture

* style(xtream-mock-server): apply repository formatting

* fix(xtream-mock-server): harden performance fixture data

* feat(xtream-mock-server): add performance control plane

* docs(performance): correct Xtream capture plan

* fix(xtream-mock-server): harden performance controls

* fix(xtream-mock-server): harden control lifecycle

* feat(performance): add Xtream preload markers

* feat(performance): trace Xtream main phases

* feat(performance): mark Xtream store publications

* feat(performance): trace Xtream database phases

* feat(performance): trace Xtream delete cancellation

* feat(performance): capture Xtream phase attribution

* feat(performance): mark Sources Xtream refresh

* test(performance): define Xtream benchmark evidence contracts

* test(performance): add Xtream benchmark runner

* test(performance): surface failure evidence writes

* test(performance): align database read clock

* test(performance): preserve capture failure contracts
2026-07-28 08:08:07 +02:00

43 KiB

Xtream Performance Benchmark 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: Add a reproducible, fail-closed Electron benchmark for large Xtream initial import, refresh, deletion, cancellation, and concurrent UI interaction, with process-separated profiles and count-only phase attribution, without changing normal production behavior.

Architecture: A performance-only 100,000-item Xtream mock fixture is materialized and hashed before capture. An opt-in, fail-neutral instrumentation layer emits sanitized renderer, preload, Electron-main, and database-worker markers. A Playwright orchestrator runs five scenarios in fresh Electron processes with exactly one warm-up, five measured iterations, and one diagnostic iteration per scenario, writes only to the ignored dist/performance/<timestamp> tree, and rejects incomplete or contaminated measurements. The existing M3U capture implementation remains behaviorally unchanged and supplies stable process/worker primitives through small, tested extensions rather than a broad rewrite.

Tech Stack: Angular 21 signals and router, Electron 41 IPC/preload, Chrome DevTools Protocol, Node inspector/perf_hooks/Worker APIs, Axios, better-sqlite3, Express, Jest, Playwright, TypeScript, Nx.


Non-Negotiable Measurement Contract

  • Formal runs use only a clean tracked worktree and exact CDP endpoint 127.0.0.1:9222. An alternate loopback CDP port is smoke-only and makes the run ineligible for before/after claims.
  • The mock binds to 127.0.0.1, uses only the committed performance-100k fixture, and rejects arbitrary provider URLs, credentials, payloads, and cardinalities. No response field used by the benchmark may reference a non-loopback URL.
  • Every formal scenario has exactly: warmup-01, run-01 through run-05, and diagnostic. Warm-up and diagnostic values never enter headline distributions.
  • Each iteration gets a fresh Electron process and data directory. Stateful refresh/delete/UI iterations seed the portal before capture in that same process, verify database idleness, and atomically roll capture into the measured operation.
  • Renderer, Electron main, and database.worker metrics remain separate. Main RSS is labelled process-wide and includes worker/native/SQLite memory; worker RSS is unavailable because a Node Worker is a thread. Native/SQLite memory is never inferred by subtracting JS heaps.
  • Xtream does not instantiate playlist-refresh.worker. Report it as N/A: not-instantiated-by-xtream-flow only after observing zero matching workers and artifacts. Any instance invalidates the iteration.
  • IPC boundary deltas are named structured-clone proxies because they include queueing/scheduling. SQLite transaction spans include commit. Index creation is N/A: schema-indexes-pre-exist; operation-creates-none.
  • Cancellation acknowledgement is the terminal database cancelled event. Also retain click-to-preload return, main HTTP abort, DB cancel dispatch, and dev-only worker-receipt timestamps as separate diagnostics.
  • Instrumentation is active only when IPTVNATOR_PERF_CAPTURE=1 and/or IPTVNATOR_PERF_WORKER_PROFILING=1, is count/identifier-only, never logs credentials/URLs/titles/search text/payloads, and is fail-neutral.
  • No production bottleneck fix belongs in this branch. This branch establishes the benchmark and instrumentation required to prove a later fix.

Scenario Matrix

Scenario ID Seed state Measured trigger Authoritative terminal
xtream-initial-import-large Empty DB, prepared mock Final Add button All three import states completed, overlay absent, expected DB counts, two painted frames
xtream-refresh-large Complete 100k import Refresh-confirm button Delete/reimport/empty restore complete, exact counts, no duplicate or stale rows
xtream-delete-large Complete 100k import Delete-confirm button DB_DELETE_PLAYLIST completed, source row absent, playlist/category/content counts zero
xtream-cancel-import Empty DB, prepared mock Final Add; cancel at first real live save progress threshold Worker receipt plus authoritative DB cancelled, visible cancelled state painted, no later VOD/series request
xtream-background-ui Complete 100k import Refresh-confirm button Fixed navigation/search/action sequence paints while DB operation is active, then normal refresh terminal

The fixture contains 60 live categories/items groups, 20 VOD groups, and 20 series groups with 1,000 items each: exactly 60,000 live + 20,000 VOD + 20,000 series = 100,000 imported catalog rows and 100 categories.

Task 0: Reconfirm Branch, Baseline, And Safety Preconditions

Files:

  • Verify only: AGENTS.md

  • Verify only: package.json

  • Verify only: pnpm-lock.yaml

  • Verify only: apps/electron-backend-e2e/project.json

  • Step 1: Confirm the linked worktree and branch

Run:

git status --short --branch
git rev-parse --show-toplevel
git rev-parse --git-common-dir
git log -1 --oneline

Expected: branch agent/perf-xtream-harness, clean tracked tree, repository root /Users/4gray/.codex/worktrees/05cf/iptvnator, and base commit 24f0dee6f033613148aeab4b699e9db667041927.

  • Step 2: Verify dependencies and Nx discovery

Run:

pnpm install --frozen-lockfile
pnpm nx show projects
pnpm nx show projects --withTarget test
pnpm nx show projects --withTarget e2e

Expected: all commands exit 0 and the project list includes electron-backend, electron-backend-e2e, portal-xtream-data-access, portal-xtream-feature, and xtream-mock-server.

  • Step 3: Record the pre-change regression baseline

Run:

pnpm nx run electron-backend-e2e:test-performance-harness --skip-nx-cache
pnpm nx test portal-xtream-data-access --skip-nx-cache
pnpm nx test portal-xtream-feature --skip-nx-cache

Expected baseline: performance harness 156/156, Xtream data-access 162 tests, Xtream feature 144 tests. Stop and use systematic-debugging if the baseline changes before editing code.

  • Step 4: Preserve the CDP blocker as an external precondition

Run:

lsof -nP -iTCP:9222 -sTCP:LISTEN || true

Do not terminate an unrelated listener without explicit user authorization. Harness implementation and unit tests may continue; no formal M3U or Xtream run may start while 9222 is occupied.

Task 1: Add A Deterministic, Local-Only 100k Xtream Fixture

Files:

  • Create: apps/xtream-mock-server/jest.config.ts

  • Create: apps/xtream-mock-server/tsconfig.spec.json

  • Create: apps/xtream-mock-server/src/app/generators/performance.generator.ts

  • Create: apps/xtream-mock-server/src/app/generators/performance.generator.spec.ts

  • Modify: apps/xtream-mock-server/project.json

  • Modify: apps/xtream-mock-server/src/app/scenarios.ts

  • Modify: apps/xtream-mock-server/src/app/data-store.ts

  • Step 1: Add the mock-server Jest target

Mirror the established stalker-mock-server Node/Jest configuration:

export default {
    displayName: 'xtream-mock-server',
    preset: '../../jest.preset.js',
    testEnvironment: 'node',
    transform: {
        '^.+\\.[tj]s$': [
            'ts-jest',
            { tsconfig: '<rootDir>/tsconfig.spec.json' },
        ],
    },
    moduleFileExtensions: ['ts', 'js', 'html'],
    coverageDirectory: '../../coverage/apps/xtream-mock-server',
};

Add an @nx/jest:jest test target with jestConfig and tsConfig paths.

  • Step 2: Write failing fixture identity tests

The tests must require:

  • scenario key performance:performance, name performance-100k, seed 91001;
  • exactly 100 categories and 100,000 catalog items with the 60k/20k/20k split;
  • unique stream/series IDs and valid category references;
  • two full resets/rebuilds serialize to identical byte counts and SHA-256;
  • every artwork/backdrop/stream URL field is empty or begins with the exact supplied http://127.0.0.1:<port> origin;
  • the account-info call does not eagerly materialize 20,000 series-detail objects; a detail is built lazily on getSeriesInfo;
  • VOD/series detail caches are keyed by portal plus item ID, not global ID.

Run and preserve the red result:

pnpm nx test xtream-mock-server --skip-nx-cache

Expected: failure because the fixture and test target do not yet exist.

  • Step 3: Implement the dedicated deterministic generator

Add this scenario shape without changing large, stress, or other existing fixtures:

'performance:performance': {
    name: 'performance-100k',
    description: 'Deterministic local-only 100k performance catalog',
    seed: 91001,
    categoryCount: { live: 60, vod: 20, series: 20 },
    itemsPerCategory: 1000,
    seasonsPerSeries: 1,
    episodesPerSeason: 1,
    accountStatus: 'Active',
    expiryDate: '2099-12-31',
    performanceFixture: 'catalog-100k',
    deferSeriesDetails: true,
},

performance.generator.ts must use no Faker date clock, Math.random, or Date.now. Derive names, ratings, timestamps, extensions, IDs, and ordering from stable integer indices plus fixed epoch 1_767_225_600 (2026-01-01 UTC). Use empty artwork/backdrop fields for the list payloads. Do not create stream URLs in catalog objects.

  • Step 4: Make performance series details lazy and cache-safe

Branch only the performance fixture in data-store.ts; leave every other scenario's output unchanged. Use ${username}:${password}:${id} cache keys for VOD/series details, and skip prepopulation when deferSeriesDetails is true.

  • Step 5: Run the green mock tests and lint
pnpm nx test xtream-mock-server --skip-nx-cache
pnpm nx lint xtream-mock-server --skip-nx-cache

Expected: all fixture identity, local-only, lazy-detail, existing mock tests, and lint checks pass.

Task 2: Add A Locked-Down Mock Performance Control Plane

Files:

  • Create: apps/xtream-mock-server/src/app/performance-control.ts

  • Create: apps/xtream-mock-server/src/app/performance-control.spec.ts

  • Create: apps/xtream-mock-server/src/app/server.ts

  • Create: apps/xtream-mock-server/src/app/server.spec.ts

  • Modify: apps/xtream-mock-server/src/main.ts

  • Modify: apps/xtream-mock-server/src/app/data-store.ts

  • Modify: apps/xtream-mock-server/README.md

  • Modify: docs/architecture/xtream-mock-server.md

  • Step 1: Write failing control-plane tests

Use a real loopback HTTP server on port 0; do not add supertest. Cover:

  • controls are absent unless IPTVNATOR_XTREAM_MOCK_CONTROL=1;
  • performance mode rejects non-loopback bind hosts;
  • every /__control/* request requires the exact per-process token header;
  • JSON bodies reject unknown fields, arbitrary scenarios/cardinalities/URLs, duplicate rules, past occurrences, and delays outside integer 0..5000;
  • prepare materializes performance-100k and returns only {epoch, scenario, seed, counts, bytes, catalogSha256};
  • reset observations clears ledger/rules but preserves the prepared fixture; reset all also clears fixture caches and increments epoch;
  • request identity is (epoch,scenario,transport,canonicalAction,categoryId|'all',occurrence), so parallel category arrival order does not affect matching;
  • barrier release and request-abort both settle and remove held responses;
  • ledger entries contain only safe action/scenario/count/status/timestamps and never password, username, raw URL, query, or response payload;
  • performance stream and /playlist.m3u endpoints return 410, while no non-loopback request is made.

Run and preserve the red result:

pnpm nx test xtream-mock-server --skip-nx-cache
  • Step 2: Extract an Express app factory

Move route wiring, without behavior changes, into:

export interface XtreamMockServerOptions {
    readonly control?: {
        readonly enabled: boolean;
        readonly token: string;
    };
    readonly host: string;
    readonly port: number;
}

export function createXtreamMockApp(
    options: XtreamMockServerOptions
): express.Express;

Keep main.ts responsible only for validated environment parsing, HTTP startup, logging, and signal/error shutdown. Normal serve behavior remains port 3211 and existing routes remain compatible.

  • Step 3: Implement bounded observations, barriers, and delays

Use an exact allowlist for canonical Xtream actions. Cap ledger and rule counts. A barrier matches before dispatch/serialization, is one-shot, and records arrived -> blocked -> responded|aborted. A delay is abort-aware and exists only for deterministic smoke/control tests; formal scenario manifests require zero delay rules.

  • Step 4: Add fixture preparation and manifest hashing

Hash stable UTF-8 JSON for the three category arrays and three catalog arrays in a documented fixed order. The hash input contains no credentials or server origin. Materialization happens before capture, so mock generation is excluded from application acquisition time.

  • Step 5: Run focused tests and docs checks
pnpm nx test xtream-mock-server --skip-nx-cache
pnpm nx lint xtream-mock-server --skip-nx-cache
pnpm exec prettier --check apps/xtream-mock-server/README.md docs/architecture/xtream-mock-server.md

Expected: green. Docs explain the opt-in control flag, loopback/token requirements, fixed scenario, prepare/reset/ledger semantics, and that delays are forbidden in formal timing.

Task 3: Add Sanitized Xtream IPC And Main-Process Phase Markers

Files:

  • Create: libs/shared/interfaces/src/lib/xtream-performance-marker.interface.ts

  • Create: apps/electron-backend/src/app/api/xtream-preload-performance-capture.ts

  • Create: apps/electron-backend/src/app/api/xtream-preload-performance-capture.spec.ts

  • Create: apps/electron-backend/src/app/events/xtream-performance.ts

  • Create: apps/electron-backend/src/app/events/xtream-performance.spec.ts

  • Modify: libs/shared/interfaces/src/index.ts

  • Modify: apps/electron-backend/src/app/api/main.preload.ts

  • Modify: apps/electron-backend/src/app/api/main.preload.performance.contract.spec.ts

  • Modify: apps/electron-backend/src/app/api/main.preload.performance.gates.spec.ts

  • Modify: apps/electron-backend/src/app/events/xtream.events.ts

  • Modify: apps/electron-backend/src/app/events/xtream.events.spec.ts

  • Step 1: Write failing schema, gating, redaction, and lifecycle tests

Target only these bridge methods:

xtreamRequest
xtreamCancelSession
dbGetCategories
dbSaveCategories
dbGetContent
dbSaveContent
dbDeleteXtreamContent
dbDeletePlaylist
dbRestoreXtreamUserData
dbSearchContent
dbGlobalSearch
dbCancelOperation

Markers may include only schema version, IPC call ID, method, boundary/outcome, epoch, playlist/operation/session identifiers, Xtream action, content/category type, and item count. Tests must reject or prove absence of URL, username, password, params, titles, search term, arrays, and result bodies. Prove exact pass-through result identity, error propagation, no marker work when disabled, and fail-neutral behavior when the marker sink throws.

Run the red tests:

pnpm nx test electron-backend --skip-nx-cache --testPathPatterns='xtream-preload-performance-capture|main.preload.performance|xtream.events|xtream-performance'
  • Step 2: Implement a separate Xtream preload marker protocol

Do not overload preload-performance-correlation.ts, whose state machine is M3U refresh-specific. Add a separate channel and schema. Extract safe identity from arguments without cloning/traversing payload arrays. Emit start and success/error around the existing proxied call only when IPTVNATOR_PERF_CAPTURE=1.

  • Step 3: Instrument main HTTP phases without changing normal Axios behavior

Emit count-only main phases:

xtream.network.total
xtream.json.transform
xtream.response.ready
xtream.cancel-session

Under capture only, wrap Axios's existing default transformResponse chain and invoke the same functions with the same context. Do not switch response type or introduce a separate manual JSON.parse. network.total contains the nested transform; the report derives acquisition as total minus JSON transform. Every success, error, and abort path must close its phase exactly once.

  • Step 4: Keep debug-copy overhead explicit

Electron imports always create a portal-debug request ID, but performance builds use the production environment and disable portal-debug sanitize/send before the payload is copied. Record that work as N/A: production-debug-send-disabled; do not infer that the request ID is absent. If a future diagnostic build enables the send path, instrument redaction/send separately and never fold that development-only copy into production acquisition.

ElectronService.forwardXtreamRequest() also posts the full returned payload through window.postMessage() after the preload call completes. Keep that second renderer clone unchanged until a baseline proves it is the selected bottleneck; attribute it through the preload-end to store-marker gap and the renderer CPU profile.

  • Step 5: Run focused backend tests and lint
pnpm nx test electron-backend --skip-nx-cache --testPathPatterns='xtream-preload-performance-capture|main.preload.performance|xtream.events|xtream-performance'
pnpm nx lint electron-backend --skip-nx-cache

Expected: green with no changes to normal IPC payloads or Axios results.

Task 4: Add Database-Worker Phase And Cancellation-Receipt Markers

Files:

  • Modify: libs/shared/interfaces/src/lib/performance-phase.interface.ts

  • Modify: apps/electron-backend/src/app/workers/worker-performance-capture.model.ts

  • Modify: apps/electron-backend/src/app/workers/worker-performance-phase.ts

  • Modify: apps/electron-backend/src/app/workers/database.worker.ts

  • Modify: apps/electron-backend/src/app/database/operations/category.operations.ts

  • Modify: apps/electron-backend/src/app/database/operations/content.operations.ts

  • Modify: apps/electron-backend/src/app/database/operations/xtream.operations.ts

  • Modify: apps/electron-backend/src/app/database/operations/playlist.operations.ts

  • Modify: apps/electron-backend/src/app/events/database/worker-ipc-contract.spec.ts

  • Modify: apps/electron-backend/src/app/services/database-worker-client.spec.ts

  • Modify: apps/electron-backend/src/app/workers/worker-performance-phase.spec.ts

  • Modify: apps/electron-backend/src/app/workers/worker-performance-capture.concurrency.spec.ts

  • Modify: apps/electron-backend/src/app/workers/worker-performance-cancellation.spec.ts

  • Modify: apps/electron-backend/src/app/workers/worker-performance-integration.spec.ts

  • Modify: apps/electron-backend/src/app/database/operations/category.operations.spec.ts

  • Modify: apps/electron-backend/src/app/database/operations/content.operations.spec.ts

  • Create: apps/electron-backend/src/app/database/operations/xtream.operations.spec.ts

  • Modify: apps/electron-backend/src/app/database/operations/playlist.operations.spec.ts

  • Step 1: Write failing phase-placement and cancellation tests

Require one ordered pair per aggregate phase, count-only metadata, closure on success/error/cancel, and no calls when worker profiling is off. Required phase names:

sqlite.categories.read
normalize.categories
sqlite.categories.write-transactions
sqlite.content.read
sqlite.content.category-map-read
normalize.content
sqlite.content.write-transactions
sqlite.xtream-cache-clear.write-transactions
sqlite.xtream-delete.collect-user-data
sqlite.xtream-delete.write-transactions
sqlite.playlist-delete.collect-ids
sqlite.playlist-delete.write-transactions
sqlite.search.query
normalize.search-rank

Wrap the complete batch loop in one aggregate write-transactions span; metadata reports total items. This avoids ambiguous repeated phase pairs. Tests must state that the span includes all transaction commits.

Cancellation cleanup runs DB_CLEAR_XTREAM_IMPORT_CACHE after an aborted import. Measure its complete delete loops with the dedicated cache-clear phase instead of hiding that work in the request envelope or mislabelling it as a content-save phase. Synthetic benchmark fixtures keep favorites/recent data empty, so restore-user-data remains explicitly N/A: no-user-data.

Require a dev-only performance-cancel-received worker message containing only operation/request IDs and epoch. It must not change DatabaseWorkerClient.cancel() into a wait or alter normal cancellation order.

  • Step 2: Extend the worker phase union generically

Change the capture helper/model to accept the union of existing app-playlist phases and new Xtream database phases. Preserve existing M3U phase strings and tests exactly.

  • Step 3: Add operation adapters without changing SQL or concurrency

Pass the optional capture adapter from database.worker.ts into the existing operations. Measure around existing normalization, query, and transaction-loop blocks. Do not reorder statements, change batch size, add indexes, serialize parallel requests, or alter transaction boundaries.

  • Step 4: Preserve overlap invalidation

registerDatabaseWorkerPerformanceCapture() must continue marking overlapping request CPU/ELU/event-loop-delay data invalid. Tests must prove no hidden serialization is introduced. Whole-worker profiles remain valid diagnostics; request-scoped overlapping metrics remain explicitly invalid.

  • Step 5: Run focused and full backend tests
pnpm nx test electron-backend --skip-nx-cache --testPathPatterns='worker-performance|worker-ipc-contract|database-worker-client|category.operations|content.operations|xtream.operations|playlist.operations'
pnpm nx test electron-backend --skip-nx-cache
pnpm nx lint electron-backend --skip-nx-cache

Expected: green and no SQL/business-result changes.

Task 5: Add Renderer Store Markers Without Equating Them To Paint

Files:

  • Modify: libs/shared/logging/src/lib/renderer-performance-phase.ts

  • Modify: libs/shared/logging/src/lib/renderer-performance-phase.spec.ts

  • Modify: libs/portal/xtream/data-access/src/lib/stores/features/with-content.feature.ts

  • Modify: libs/portal/xtream/data-access/src/lib/stores/features/with-content.feature.spec.ts

  • Modify: libs/portal/xtream/data-access/src/lib/stores/features/with-search.feature.ts

  • Modify: libs/portal/xtream/data-access/src/lib/stores/features/with-search.feature.spec.ts

  • Modify: libs/playlist/shared/ui/src/lib/playlist-refresh-action.service.ts

  • Modify: libs/playlist/shared/ui/src/lib/playlist-refresh-action.service.spec.ts

  • Modify: libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.ts

  • Modify: libs/playlist/shared/ui/src/lib/recent-playlists/recent-playlists.component.spec.ts

  • Step 1: Write failing opt-in/fail-neutral store-marker tests

Add count-only phase names:

store.xtream-publish-categories
store.xtream-publish-live
store.xtream-publish-vod
store.xtream-publish-series
store.xtream-import-terminal
store.xtream-search-results
store.xtream-refresh-meta
store.xtream-delete-row

Tests must prove the marker wraps only the synchronous patchState or NgRx dispatch, emits one start/end pair, preserves thrown errors/results, performs no metadata work without a hook, and ignores hook failures.

  • Step 2: Wrap the exact existing publication sites

Use measureRendererPerformancePhase around the category publication at with-content.feature.ts:965, live/VOD/series publications at :1047/:1083/:1119, import-terminal publication, search result publication, refresh metadata dispatch, and delete-row dispatch. Do not change fetch order, signals, routes, or visible behavior.

  • Step 3: Keep Angular paint external to store timing

Document in code comments and benchmark contracts that a synchronous store span is not render time. The Playwright renderer probe will observe a scenario-specific DOM sentinel and two requestAnimationFrame callbacks after the marker end.

  • Step 4: Run affected project tests
pnpm nx test shared-logging --skip-nx-cache
pnpm nx test portal-xtream-data-access --skip-nx-cache
pnpm nx test playlist-shared-ui --skip-nx-cache

If the exact project names differ, discover them with:

pnpm nx show projects --withTarget test

Then run the owning targets; do not guess a project name.

Task 6: Extend The Existing Process Capture For Xtream Attribution

Files:

  • Create: apps/electron-backend-e2e/src/performance/xtream-ipc-marker-events.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-ipc-marker-events.spec.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-worker-phase-events.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-worker-phase-events.spec.ts

  • Modify: apps/electron-backend-e2e/src/performance/m3u-refresh-main-capture.ts

  • Modify: apps/electron-backend-e2e/src/performance/worker-performance-phase-events.ts

  • Modify: apps/electron-backend-e2e/src/performance/worker-request-performance.ts

  • Modify: apps/electron-backend-e2e/src/performance/worker-request-performance-envelope.spec.ts

  • Modify: apps/electron-backend-e2e/src/performance/performance-phase-events.ts

  • Modify: apps/electron-backend-e2e/src/performance/performance-phase-events.spec.ts

  • Step 1: Write failing parsing/correlation tests

Require:

  • exact schema validation and count-only fields;

  • ordered start/end pairs and no unclosed/duplicate boundaries;

  • FIFO correlation by safe method/playlist/type/action/operation key;

  • quarantine rather than guessing when the same key overlaps;

  • handler/preload/worker timestamps remain monotonic;

  • acquisition = network.total - json.transform, never negative;

  • main-to-worker and worker-to-main deltas are named IPC proxies;

  • worker cancel receipt and terminal cancel are separate;

  • existing M3U parsers produce byte-equivalent normalized output.

  • Step 2: Parameterize main capture phase subscriptions

Extend installMainCapture() to subscribe to both existing M3U and new Xtream diagnostics channels. Keep the old state key and exported API for compatibility; do not rename or copy the 1,500-line injected protocol in this task. Add only the minimum channel allowlists and timeline record shapes.

  • Step 3: Capture Xtream preload and main-handler boundaries

Listen for sanitized preload markers in main and record handler receive/ready phases. Match DB worker requests using safe operation ID where supplied and FIFO/quarantine otherwise. Never attach raw IPC payloads to the timeline.

  • Step 4: Record true worker cancellation receipt

Recognize performance-cancel-received without counting it as a normal response. Store the epoch/IDs in the current capture generation. Late or cross-generation markers invalidate cancellation attribution.

  • Step 5: Run the complete performance-harness unit suite
pnpm nx run electron-backend-e2e:test-performance-harness --skip-nx-cache

Expected: all old M3U tests and new Xtream contracts pass.

Task 7: Build Xtream Scenario, Summary, And Validity Contracts

Files:

  • Create: apps/electron-backend-e2e/src/performance/xtream-benchmark-contract.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-benchmark-contract.spec.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-control-client.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-control-client.spec.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-summary-contract.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-phase-attribution.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-phase-attribution.spec.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-benchmark-report.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-benchmark-report.spec.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-diagnostic-artifacts.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-diagnostic-artifacts.spec.ts

  • Step 1: Write failing schedule and safety tests

Require the five exact scenario IDs and formal iteration schedule:

[
    { runId: 'warmup-01', kind: 'warmup' },
    { runId: 'run-01', kind: 'measured' },
    { runId: 'run-02', kind: 'measured' },
    { runId: 'run-03', kind: 'measured' },
    { runId: 'run-04', kind: 'measured' },
    { runId: 'run-05', kind: 'measured' },
    { runId: 'diagnostic', kind: 'diagnostic' },
];

Smoke may reduce measured runs to one but must set eligibleForComparison=false. Reject non-loopback origin, unexpected fixture name/hash/counts, any credential field in serialized artifacts, nonzero formal delay/barrier residue, and any unexpected mock action.

  • Step 2: Define the request and phase contract

After observations reset, initial/reimport expects exactly seven application actions:

get_account_info
get_live_categories
get_vod_categories
get_series_categories
get_live_streams
get_vod_streams
get_series

Category order is irrelevant; content order is live -> VOD -> series. Cancel requires account + categories + live only, with no VOD/series request after the authoritative cancellation point.

  • Step 3: Define process-separated result types

Each result stores:

  • total and named phase durations;

  • renderer peak/post-GC heap, long-task count/max, frame-gap p95/max, heartbeat p95/max, store-to-paint, navigation/search/action latency;

  • main CPU, peak/post-GC heap, process-wide peak/post-GC RSS, ELD p95/p99/max, ELU/unavailable reason, BrowserWindow unresponsive/responsive events;

  • database-worker CPU, peak/post-GC heap, external memory, ELD p95/p99/max, ELU, request validity, profile paths, and RSS N/A reason;

  • playlist-refresh worker N/A reason;

  • cancel dispatch/receipt/terminal/paint latencies;

  • explicit index, exact-commit, native-memory, and debug-copy N/A/proxy labels.

  • Step 4: Fail closed on comparison validity

Formal summaries require five valid measured values for every required metric; never silently filter nulls. Require one renderer identity, one current database worker, zero playlist-refresh workers, no late DB request after capture cutoff, complete action/DB phase contracts, exact catalog invariants, no console errors, and valid regular contained diagnostic artifacts.

  • Step 5: Run the harness unit suite
pnpm nx run electron-backend-e2e:test-performance-harness --skip-nx-cache

Task 8: Build The Renderer Probe And Initial/Cancel Drivers

Files:

  • Create: apps/electron-backend-e2e/src/performance/xtream-renderer-probe.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-renderer-probe.spec.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-renderer-capture.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-scenario-driver.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-scenario-driver.spec.ts

  • Step 1: Write failing probe lifecycle tests

Mirror the proven M3U capture lifecycle and require:

  • Long Tasks clipped to operation boundaries;

  • continuous rAF gaps and a 50 ms deadline-based UI heartbeat;

  • 20 ms renderer heap sampling plus explicit GC/post-GC heap;

  • route, store marker, DOM sentinel, and two-rAF paint timestamps;

  • idempotent stop/dispose after partial startup failure;

  • CPU/trace/heap snapshot only in diagnostic;

  • no profile path in measured/warm-up runs.

  • Step 2: Implement initial-import trigger and terminal

Prepare the Xtream dialog with only the fixed synthetic name, loopback origin, and committed synthetic credential pair. Start clocks immediately before the final Add button activation. Terminal requires overlay absent, all three import-status values completed, exact 100k content/100 category DB counts, and two rendered frames after the final store marker.

  • Step 3: Implement cancellation at real DB progress

Install the DB-event listener before triggering import. On the first live save-content progress event at or above max(100, floor(total * 0.10)), synchronously record the click epoch and activate the visible cancel button. Use no timeout/delay as the trigger. Require worker receipt, terminal DB cancelled, painted cancelled state, no later success, no automatic retry, and no VOD/series mock requests.

  • Step 4: Add interaction-free smoke driver tests

Use deterministic mocked Page/CDP/main-capture adapters to prove trigger order, terminal checks, cleanup, and that credential strings never enter result, manifest, console label, or artifact filenames.

  • Step 5: Run the complete harness tests
pnpm nx run electron-backend-e2e:test-performance-harness --skip-nx-cache

Task 9: Build Refresh, Delete, And Background-UI Drivers

Files:

  • Modify: apps/electron-backend-e2e/src/performance/xtream-scenario-driver.ts

  • Modify: apps/electron-backend-e2e/src/performance/xtream-scenario-driver.spec.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-ui-action-probe.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-ui-action-probe.spec.ts

  • Step 1: Write failing seed/rollover tests

Require a complete seed import outside measured capture, an idle DB worker, zero pending requests, and atomic main-capture rollover before trigger. Store seed metrics separately as seed-main-capture.json; never include them in measured summaries.

  • Step 2: Implement refresh

Start at the refresh confirmation button. Include DB_DELETE_XTREAM_CONTENT, playback/meta parallel work, navigation, the seven mock actions, three category writes, three content writes, empty user-data restore, final store publication, and paint. Assert exact counts and no duplicate/stale rows.

  • Step 3: Implement full source deletion

Start at the delete confirmation button. Require started/progress/completed DB events, source row removal, zero playlist/category/content rows, and no stale active-store route. Capture all delete transaction phases and final dispatch to paint.

  • Step 4: Implement the fixed background UI sequence

Start a natural delay-zero refresh. Only after proving the DB delete operation active, perform:

  1. Sources filter input and result paint;
  2. navigation to Dashboard and two-rAF route paint;
  3. navigation back to Sources;
  4. source search input and result paint;
  5. one stable, non-destructive UI action.

Record input/click to paint latency for every action and require the background operation to be active both before and after each action. If it finishes too early, invalidate the iteration and increase the fixed fixture cardinality in a separately reviewed change; never add sleeps or mock delay to formal timing.

  • Step 5: Run harness and existing responsiveness tests
pnpm nx run electron-backend-e2e:test-performance-harness --skip-nx-cache
pnpm nx run electron-backend-e2e:e2e-ci--src/xtream-responsiveness.e2e.ts --skip-nx-cache

The existing E2E may keep its deliberate worker batch delay because it is a functional regression test. The benchmark must always set IPTVNATOR_DB_WORKER_BATCH_DELAY_MS=0.

Task 10: Add The Orchestrator, Nx Target, And Artifact Validation

Files:

  • Create: apps/electron-backend-e2e/src/performance/xtream-benchmark-orchestrator.ts

  • Create: apps/electron-backend-e2e/src/performance/xtream-benchmark-lifecycle.spec.ts

  • Create: apps/electron-backend-e2e/src/xtream.performance.ts

  • Create: apps/electron-backend-e2e/playwright.xtream-performance.config.ts

  • Modify: apps/electron-backend-e2e/project.json

  • Modify: apps/electron-backend-e2e/src/performance/initial-import-diagnostic-artifacts.ts

  • Step 1: Write failing lifecycle and artifact tests

Require:

  • preflight before directory creation;

  • mock prepare before capture and observations reset immediately before trigger;

  • fresh app/data dir per iteration and guaranteed cleanup;

  • raw iteration JSON written before summary validity throws;

  • diagnostic regular/non-symlink/nonempty JSON trace, CPU profile, and heap snapshot for renderer/main/database worker;

  • no playlist-refresh worker artifacts;

  • source-map/build hash manifest sufficient to map profiles after a rebuild;

  • output contained by a fresh absolute dist/performance/<timestamp>/<variant> path;

  • no .cpuprofile, .heapsnapshot, trace, or generated summary staged by Git.

  • Step 2: Implement the serial orchestrator

For each scenario, run all seven definitions serially. Launch Electron with:

--js-flags=--expose-gc
--remote-debugging-address=127.0.0.1
--remote-debugging-port=9222
--user-data-dir=<fresh temp path>

Set:

IPTVNATOR_DB_WORKER_BATCH_DELAY_MS=0
IPTVNATOR_PERF_CAPTURE=1
IPTVNATOR_PERF_WORKER_PROFILING=1
IPTVNATOR_TRACE_RENDERER_CONSOLE=0

Start renderer/main capture before trigger, wait for authoritative terminal and DB settlement, stop capture, write raw capture/result JSON, validate artifacts, then build the scenario summary.

  • Step 3: Add the isolated mock web server

playwright.xtream-performance.config.ts starts exactly one control-enabled mock on a dedicated loopback port with a per-process token, reuseExistingServer: false, one Playwright worker, no retries, and a 30-minute test timeout. The token is passed to the control client but never serialized.

  • Step 4: Add the Nx target

Add benchmark-xtream with:

{
    "dependsOn": ["electron-backend:build-performance"],
    "executor": "nx:run-commands",
    "cache": false,
    "parallelism": false,
    "options": {
        "cwd": "apps/electron-backend-e2e",
        "command": "pnpm exec playwright test --config=playwright.xtream-performance.config.ts src/xtream.performance.ts"
    }
}
  • Step 5: Run all harness tests
pnpm nx run electron-backend-e2e:test-performance-harness --skip-nx-cache

Expected: old M3U and new Xtream lifecycle/contract tests pass.

Task 11: Run A Non-Claim Smoke And Close The Harness PR

Files:

  • Modify: docs/architecture/xtream-mock-server.md

  • Modify: docs/architecture/sqlite-db-worker.md

  • Modify if command/environment text changed: AGENTS.md

  • Modify if command/environment text changed: CLAUDE.md

  • Verify only: .gitignore

  • Verify only: .changes/README.md

  • Step 1: Run the full validation ladder

pnpm nx test xtream-mock-server --skip-nx-cache
pnpm nx test electron-backend --skip-nx-cache
pnpm nx test portal-xtream-data-access --skip-nx-cache
pnpm nx test portal-xtream-feature --skip-nx-cache
pnpm nx run electron-backend-e2e:test-performance-harness --skip-nx-cache
pnpm nx run electron-backend-e2e:e2e-ci--src/xtream-responsiveness.e2e.ts --skip-nx-cache
pnpm nx run electron-backend-e2e:e2e-ci--src/sources.e2e.ts --skip-nx-cache
pnpm nx run electron-backend-e2e:e2e-ci--src/search.e2e.ts --skip-nx-cache
pnpm nx affected -t lint --base=24f0dee6f033613148aeab4b699e9db667041927 --head=HEAD --skip-nx-cache
pnpm run release:notes:validate
  • Step 2: Run a smoke benchmark only when a CDP port is available

If 9222 remains occupied, use an unused alternate loopback port only for smoke:

perf_output="$PWD/dist/performance/$(date -u +%Y%m%dT%H%M%SZ)-xtream-smoke"
IPTVNATOR_PERF_OUTPUT_DIR="$perf_output" \
IPTVNATOR_PERF_VARIANT=smoke \
IPTVNATOR_PERF_SMOKE=1 \
IPTVNATOR_PERF_CDP_PORT=9322 \
NX_TASKS_RUNNER_DYNAMIC_OUTPUT=false \
pnpm nx run electron-backend-e2e:benchmark-xtream --skip-nx-cache

Validate that every smoke summary says eligibleForComparison=false. Never quote smoke values as baseline or improvement evidence.

  • Step 3: Update canonical docs

Document the future formal command, fixture identity, exact run schedule, process/RSS/heap distinctions, Xtream worker topology, control-plane safety, artifact layout, validity gates, and before/after rules. Keep AGENTS.md and CLAUDE.md mirrored if their tracing/benchmark environment section changes.

  • Step 4: Test-impact and release-note decision

This PR changes only opt-in development/test instrumentation and synthetic mock tooling; normal user-visible behavior is unchanged. Do not add a .changes note. Apply no-release-note when opening the PR because runtime paths under apps/** and libs/** are touched.

  • Step 5: Verify tracked scope and ignored raw artifacts
git status --short
git check-ignore -v dist/performance
git diff --check
git diff --stat 24f0dee6f033613148aeab4b699e9db667041927
git ls-files 'dist/performance/**'

Expected: tracked source/tests/docs only; git ls-files prints nothing.

  • Step 6: Commit, push, open the PR, and run the review loop

Use focused conventional commits, push agent/perf-xtream-harness, open a ready PR with validation and smoke caveats, apply no-release-note, trigger @codex review, and inspect all CI/review threads. Do not merge until every actionable item is fixed, re-tested, independently reviewed, and current-head CI is green.

Task 12: Run The Formal Xtream Baseline After Harness Merge

Files:

  • Generated only, ignored: dist/performance/<timestamp>-xtream/baseline/**

  • Step 1: Start from the exact clean merged commit

git switch master
git pull --ff-only
git status --short
lsof -nP -iTCP:9222 -sTCP:LISTEN

Expected: clean tree and port 9222 free. If an unrelated listener remains, stop and request authorization rather than killing it.

  • Step 2: Run the formal benchmark
perf_output="$PWD/dist/performance/$(date -u +%Y%m%dT%H%M%SZ)-xtream"
IPTVNATOR_PERF_OUTPUT_DIR="$perf_output" \
IPTVNATOR_PERF_VARIANT=baseline \
NX_TASKS_RUNNER_DYNAMIC_OUTPUT=false \
pnpm nx run electron-backend-e2e:benchmark-xtream --skip-nx-cache

Do not set smoke or alternate-port flags.

  • Step 3: Validate and analyze raw profiles

Verify every scenario has one warm-up, five measured, one diagnostic, valid process identities, exact fixture/action hashes, zero external requests, and all required artifacts. Run the ignored CPU/heap analyzers sequentially, preserve source-mapped hotspot and retaining-path JSON beside the raw profiles, and hash the analyzer/source-map inputs.

  • Step 4: Name and prove three bottlenecks

For each candidate, cite:

  • scenario and measured phase distribution;
  • renderer/main/database-worker CPU stack;
  • retaining path and retained/self sizes with process scope;
  • corroborating event-loop/long-task/frame/heartbeat evidence;
  • explicit confounders and attribution gaps.

Choose only the strongest candidate. Add a regression test that fails at the merged baseline, implement the smallest production change, rerun all affected tests, and repeat the identical formal benchmark into a fresh after directory.

  • Step 5: Claim improvement only after matched five-run evidence

Report before/after medians and p95 where appropriate, percentage change, memory/process separation, variance, unchanged/counter-regressed metrics, and tradeoffs. If the matched results do not improve, revert or describe the change as unconfirmed; never call it an optimization.