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

1043 lines
43 KiB
Markdown

# 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:
```bash
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:
```bash
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:
```bash
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:
```bash
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:
```ts
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:
```bash
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:
```ts
'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**
```bash
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:
```bash
pnpm nx test xtream-mock-server --skip-nx-cache
```
- [ ] **Step 2: Extract an Express app factory**
Move route wiring, without behavior changes, into:
```ts
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**
```bash
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:
```text
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:
```bash
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:
```text
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**
```bash
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:
```text
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**
```bash
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:
```text
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**
```bash
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:
```bash
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**
```bash
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:
```ts
[
{ 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:
```text
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**
```bash
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**
```bash
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**
```bash
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:
```text
--js-flags=--expose-gc
--remote-debugging-address=127.0.0.1
--remote-debugging-port=9222
--user-data-dir=<fresh temp path>
```
Set:
```text
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:
```json
{
"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**
```bash
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**
```bash
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:
```bash
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**
```bash
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**
```bash
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**
```bash
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.