* 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
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 committedperformance-100kfixture, 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-01throughrun-05, anddiagnostic. 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.workermetrics 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 asN/A: not-instantiated-by-xtream-flowonly 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
cancelledevent. 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=1and/orIPTVNATOR_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, nameperformance-100k, seed91001; - 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; preparematerializesperformance-100kand returns only{epoch, scenario, seed, counts, bytes, catalogSha256};reset observationsclears ledger/rules but preserves the prepared fixture;reset allalso 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.m3uendpoints return410, 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/Areason; -
playlist-refresh worker
N/Areason; -
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:
- Sources filter input and result paint;
- navigation to Dashboard and two-rAF route paint;
- navigation back to Sources;
- source search input and result paint;
- 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.