diff --git a/.github/actions/install-node-dependencies/action.yml b/.github/actions/install-node-dependencies/action.yml index 7695d2bec9b..a31ed45eefe 100644 --- a/.github/actions/install-node-dependencies/action.yml +++ b/.github/actions/install-node-dependencies/action.yml @@ -10,6 +10,10 @@ inputs: description: Node.js version override; defaults to the version declared in package.json. required: false default: '' + cache-dependency-path: + description: Lockfiles for the pnpm download store; include mobile/pnpm-lock.yaml only when the job installs mobile dependencies. + required: false + default: pnpm-lock.yaml persist-native-cache: description: Save restored native modules at job end. Set false when a later step overwrites the same path with a different ABI. required: false @@ -39,9 +43,7 @@ runs: with: install: false - # Why both lockfiles: setup-node keys the pnpm store on the root lockfile alone, so - # jobs that also install mobile restored a store with none of the React Native tree - # in it and re-downloaded the lot on every run. + # Desktop-only jobs should not miss their download cache when mobile dependencies change. - name: Setup Node.js id: default-node if: inputs.node-version == '' @@ -49,9 +51,7 @@ runs: with: node-version-file: package.json cache: pnpm - cache-dependency-path: | - pnpm-lock.yaml - mobile/pnpm-lock.yaml + cache-dependency-path: ${{ inputs.cache-dependency-path }} - name: Setup requested Node.js id: requested-node @@ -60,9 +60,7 @@ runs: with: node-version: ${{ inputs.node-version }} cache: pnpm - cache-dependency-path: | - pnpm-lock.yaml - mobile/pnpm-lock.yaml + cache-dependency-path: ${{ inputs.cache-dependency-path }} - name: Validate native runtime shell: bash @@ -152,9 +150,9 @@ runs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} - name: Restore compiled native modules without saving id: native-cache-restore-only @@ -163,9 +161,9 @@ runs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.native-cache-scope.outputs.scope }}-${{ runner.arch }}-${{ inputs.native-runtime }}-node${{ steps.requested-node.outputs.node-version || steps.default-node.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} # pnpm's bundled gyp_main.py is not executable on fresh Linux runners. - name: Use external node-gyp diff --git a/.github/workflows/adhoc-mac-build.yml b/.github/workflows/adhoc-mac-build.yml index 3e17eee9b68..ac49acdbb83 100644 --- a/.github/workflows/adhoc-mac-build.yml +++ b/.github/workflows/adhoc-mac-build.yml @@ -160,16 +160,14 @@ jobs: - name: Checkout the requested ref uses: actions/checkout@v6 - env: - # Full-history checkout must also preserve case-twin branch and tag names. - GIT_DEFAULT_REF_FORMAT: reftable with: # Why an input at all rather than just github.ref: the whole point is to # build code that has not landed, and the workflow definition itself # always comes from the dispatch ref — naming the branch here instead # applies main's current copy of this file to an arbitrary branch. ref: ${{ steps.vetted.outputs.sha }} - fetch-depth: 0 + # Version helpers only read HEAD; published versions come from the release API. + fetch-depth: 1 # This job only reads stablyai/orca and never pushes; every write goes # to the adhoc repo through a minted App token passed by env. Not # persisting the checkout credential shrinks the blast radius if a build diff --git a/.github/workflows/cloud-deploy-relay-production-same-cap.yml b/.github/workflows/cloud-deploy-relay-production-same-cap.yml index 50d609b30d3..31fe9bfb203 100644 --- a/.github/workflows/cloud-deploy-relay-production-same-cap.yml +++ b/.github/workflows/cloud-deploy-relay-production-same-cap.yml @@ -62,7 +62,7 @@ on: required: false type: string canary-run-id: - description: Successful same-commit canary run required for batch-apply + description: Successful same-code canary in this rehome control generation; reusable across batches required: false type: string confirmation: diff --git a/.github/workflows/daily-mac-build.yml b/.github/workflows/daily-mac-build.yml index 45130e932d8..e70fc928295 100644 --- a/.github/workflows/daily-mac-build.yml +++ b/.github/workflows/daily-mac-build.yml @@ -90,7 +90,8 @@ jobs: uses: actions/checkout@v6 with: ref: main - fetch-depth: 0 + # Version helpers only read HEAD; published versions come from the release API. + fetch-depth: 1 # Why: this job only reads stablyai/orca and never pushes; every write # goes to the daily repo through a minted App token passed by env. # Not persisting the checkout credential shrinks the blast radius if a diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a07bf0991ec..f42d1184ce9 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -170,8 +170,34 @@ jobs: # artifact instead of starting five concurrent electron-vite builds. # ORCA_E2E_FORWARD_APP_LOGS keeps startup failures visible when Electron # launches but never creates a BrowserWindow. + - name: Balance E2E shard from timing evidence + env: + ORCA_BACKGROUND_LAUNCH: '1' + SKIP_BUILD: '1' + ORCA_E2E_FORWARD_APP_LOGS: '1' + ORCA_E2E_WEB_CLIENT: '1' + ORCA_RELAY_PATH: ${{ github.workspace }}/out/relay + run: | + mkdir -p ci-shards + pnpm exec playwright test --config tests/playwright.config.ts --project=electron-headless --list --reporter=json > ci-shards/discovery.json + export ORCA_SHARD_SOURCE_SHA="$(git rev-parse HEAD)" + node config/scripts/ci-e2e-shard-plan.mjs ci-shards/discovery.json '${{ matrix.shard }}' ci-shards + pnpm exec playwright test --config tests/playwright.config.ts --project=electron-headless --test-list=ci-shards/selected.txt --list --reporter=json > ci-shards/selected-discovery.json + node config/scripts/ci-e2e-shard-plan.mjs --verify ci-shards/assignment.json ci-shards/selected-discovery.json + - name: Run E2E tests (${{ matrix.shard_name }}) - run: xvfb-run --auto-servernum bash .github/scripts/e2e-with-window-manager.sh env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 ORCA_E2E_WEB_CLIENT=1 ORCA_RELAY_PATH="$GITHUB_WORKSPACE/out/relay" pnpm run test:e2e --shard=${{ matrix.shard }} + run: xvfb-run --auto-servernum bash .github/scripts/e2e-with-window-manager.sh env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 ORCA_E2E_WEB_CLIENT=1 ORCA_RELAY_PATH="$GITHUB_WORKSPACE/out/relay" pnpm run test:e2e --test-list=ci-shards/selected.txt + + - name: Upload E2E shard assignment + if: always() + # Diagnostic upload outages must not change the test verdict. + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: e2e-shard-${{ matrix.shard_name }}-attempt-${{ github.run_attempt }} + path: ci-shards/ + retention-days: 14 + if-no-files-found: warn # The frame benchmark needs a mapped window, which the headless shards exclude. - name: Run worktree first-paint benchmark diff --git a/.github/workflows/hourly-mac-build.yml b/.github/workflows/hourly-mac-build.yml index c300b2543b8..e55adc54995 100644 --- a/.github/workflows/hourly-mac-build.yml +++ b/.github/workflows/hourly-mac-build.yml @@ -137,7 +137,8 @@ jobs: uses: actions/checkout@v6 with: ref: ${{ needs.preflight.outputs.head_sha }} - fetch-depth: 0 + # Version helpers only read HEAD; published versions come from the release API. + fetch-depth: 1 # Why: this job only reads stablyai/orca and never pushes; every write # goes to the hourly repo through a minted App token passed by env. # Not persisting the checkout credential shrinks the blast radius if a diff --git a/.github/workflows/mobile-ios-release.yml b/.github/workflows/mobile-ios-release.yml index 934b3f694a3..dd9a265d0c8 100644 --- a/.github/workflows/mobile-ios-release.yml +++ b/.github/workflows/mobile-ios-release.yml @@ -94,6 +94,8 @@ jobs: run: node -e 'const fs = require("node:fs"); const { expo } = require("./app.json"); fs.appendFileSync(process.env.GITHUB_OUTPUT, `version=${expo.version}\nbuild_number=${expo.ios.buildNumber}\n`)' - name: Expo prebuild + env: + ORCA_IOS_APS_ENVIRONMENT: production run: npx expo prebuild --platform ios --no-install - name: Install CocoaPods diff --git a/.github/workflows/mobile.yml b/.github/workflows/mobile.yml index 6dbfc02aa3c..9afc865f87e 100644 --- a/.github/workflows/mobile.yml +++ b/.github/workflows/mobile.yml @@ -41,6 +41,10 @@ jobs: uses: actions/checkout@v6 - uses: ./.github/actions/install-node-dependencies + with: + cache-dependency-path: | + pnpm-lock.yaml + mobile/pnpm-lock.yaml # bundler-cache installs mobile/Gemfile.lock, so this job is also what # proves the pinned fastlane the release workflow depends on still diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c49091ab148..a4e0d08ec6d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -126,6 +126,9 @@ jobs: - uses: ./.github/actions/install-node-dependencies with: native-runtime: node + cache-dependency-path: | + pnpm-lock.yaml + mobile/pnpm-lock.yaml - name: Lint run: pnpm exec oxlint --format github @@ -273,6 +276,12 @@ jobs: HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: node .github/scripts/check-root-directory-entries.mjs "$BASE_SHA" "$HEAD_SHA" + # Why here: the READMEs embed media owned by docs/site and resources/onboarding, + # and the classifier skips static_analysis for docs-only diffs. This job runs + # on every PR and needs no install. + - name: Check README local links + run: node config/scripts/check-readme-local-links.mjs + typecheck: needs: [code_paths] if: needs.code_paths.outputs.typecheck == 'true' @@ -775,18 +784,9 @@ jobs: [[ "$rpm_marker" == rpm ]] || { echo "Expected rpm marker, got: $rpm_marker"; exit 1; } - name: Verify headless serve signal shutdown - run: node config/scripts/run-headless-serve-shutdown-docker.mjs --appimage dist/orca-linux.AppImage - - - name: Verify extracted launcher serve signal shutdown run: >- node config/scripts/run-headless-serve-shutdown-docker.mjs - --appimage dist/orca-linux.AppImage --entrypoint launcher - - - name: Verify AppImage CLI registration and serve signal shutdown - run: >- - node config/scripts/run-headless-serve-shutdown-docker.mjs - --appimage dist/orca-linux.AppImage --entrypoint appimage - --signal-target serving-electron --int-delivery pid + --appimage dist/orca-linux.AppImage --all-entrypoints # A default container reproduces the hostile AppImage launch environment. - name: Verify Linux CLI launch contract @@ -835,9 +835,9 @@ jobs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-node-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-node-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} # vitest runs here directly rather than through `pnpm test`, so the addon # assertions only hold once install-node-dependencies has rebuilt natives. @@ -846,6 +846,7 @@ jobs: pnpm exec vitest run --config config/vitest.config.ts config/scripts/rebuild-native-deps.test.mjs config/scripts/rebuild-native-deps-windows-process-tree.test.mjs + src/main/windows-registry-addon.test.ts src/main/browser/browser-client-page-renderer-lifecycle.electron.test.ts src/main/browser/browser-route-tcp-egress.electron.test.ts src/main/browser/browser-route-webrtc-egress.electron.test.ts @@ -903,9 +904,9 @@ jobs: with: path: | node_modules/.pnpm/node-pty@*/node_modules/node-pty/build - node_modules/.pnpm/windows-native-registry@*/node_modules/windows-native-registry/build + node_modules/.pnpm/@orca+windows-registry@*/node_modules/@orca/windows-registry/build node_modules/.pnpm/@vscode+windows-process-tree@*/node_modules/@vscode/windows-process-tree/build - key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-electron-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch') }} + key: native-modules-${{ runner.os }}-${{ steps.deps.outputs.native-cache-scope }}-${{ runner.arch }}-electron-node${{ steps.deps.outputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', '.github/actions/install-node-dependencies/action.yml', 'config/scripts/ensure-native-runtime.mjs', 'config/scripts/rebuild-native-deps.mjs', 'config/patches/node-pty@1.1.0.patch', 'config/patches/@vscode__windows-process-tree@0.8.0.patch', 'native/windows-registry/src/addon.cc', 'native/windows-registry/binding.gyp', 'native/windows-registry/package.json') }} - name: Prepare Electron native runtime run: node config/scripts/ensure-native-runtime.mjs --runtime=electron diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 0c215db94d3..b21feae3230 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -45,7 +45,11 @@ jobs: npx --yes pnpm@10.24.0 --filter '@orca-cloud/relay^...' build - name: Test shard + env: + ORCA_BALANCE_UNIT_SHARDS: '1' + ORCA_BACKGROUND_LAUNCH: '1' run: | + export ORCA_SHARD_SOURCE_SHA="$(git rev-parse HEAD)" pnpm exec vitest run --config config/vitest.config.ts \ --exclude=src/main/daemon/repro-13767-shell-ready-marker-lost-to-exec.test.ts \ --exclude=src/main/daemon/shell-ready.test.ts \ @@ -65,3 +69,14 @@ jobs: --exclude=src/shared/posix-command-path-lookup.test.ts \ --exclude=tests/e2e/cross-version-wire/** \ --shard=${{ matrix.shard }}/${{ matrix.shard_total }} + + - name: Upload unit shard assignment + if: always() + # Diagnostic upload outages must not change the test verdict. + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: unit-shard-node-${{ matrix.node }}-${{ matrix.shard }}-attempt-${{ github.run_attempt }} + path: ci-shards/ + retention-days: 14 + if-no-files-found: warn diff --git a/.gitignore b/.gitignore index e5207a25015..52479fe20e7 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ out/ /build/ release/ native/**/.build/ +# node-gyp output for the vendored Windows registry addon; generated per host and ABI. +native/windows-registry/build/ +native/windows-registry/bin/ # pnpm .pnpm-store/ @@ -104,6 +107,7 @@ docs/** !docs/mobile-terminal-shortcut-bar.md !docs/reference/ !docs/reference/agent-pty-transcript-capture.md +!docs/reference/agent-session-search-query-tuning.md !docs/reference/agent-status-store.md !docs/reference/antigravity-readiness-evidence.md !docs/reference/git-compatibility.md diff --git a/README.md b/README.md index 7e3540c80f1..aeb8f355450 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Fan one prompt across five agents, each in its own isolated git worktree — com - Parallel worktree orchestration + Parallel worktree orchestration @@ -68,7 +68,7 @@ Ghostty-class terminals with WebGL rendering, infinite splits, and scrollback th - Terminal splits + Terminal splits @@ -82,7 +82,7 @@ Click any UI element in a real Chromium window to send its HTML, CSS, and a crop - Embedded browser and Design Mode + Embedded browser and Design Mode @@ -96,7 +96,7 @@ Browse PRs, issues, and project boards in-app — open a worktree from any task - GitHub and Linear task workflows in Orca + GitHub and Linear task workflows in Orca @@ -110,7 +110,7 @@ Run agents on a beefy remote box with full file editing, git, and terminals — - Remote worktrees over SSH + Remote worktrees over SSH @@ -124,7 +124,7 @@ Drop comments on any diff line and ship them back to the agent — review, edit, - Annotate AI-generated diffs + Annotate AI-generated diffs @@ -138,7 +138,7 @@ VS Code's editor with autosave everywhere — drag files or images straight into - Drag files and images into an agent prompt + Drag files and images into an agent prompt @@ -152,7 +152,7 @@ Agents drive Orca too — script every workflow with `orca worktree create`, `sn - Script Orca from the CLI + Script Orca from the CLI diff --git a/cloud/apps/relay-ops/src/incident-monitor.test.ts b/cloud/apps/relay-ops/src/incident-monitor.test.ts index c1a073cde4a..be35b55f65b 100644 --- a/cloud/apps/relay-ops/src/incident-monitor.test.ts +++ b/cloud/apps/relay-ops/src/incident-monitor.test.ts @@ -273,6 +273,26 @@ describe('incident monitor evaluator', () => { ) }) + it('allows at most three unexpected director errors per five minutes without relaxing other gates', () => { + for (const errors of [1, 2, 3]) { + const sample = healthySample() + sample.sources['cloud-monitoring']!.signals['director.errors'] = signal(errors) + expect(evaluateIncidentSample(sample, startedAt).status).toBe('green') + } + const excess = healthySample() + excess.sources['cloud-monitoring']!.signals['director.errors'] = signal(4) + expect(evaluateIncidentSample(excess, startedAt).failures).toContainEqual( + expect.objectContaining({ signal: 'director.errors', observed: 4, threshold: 3 }) + ) + const auth = healthySample() + auth.sources['cloud-monitoring']!.signals['auth.errors'] = signal(1) + expect(evaluateIncidentSample(auth, startedAt).status).toBe('freeze') + const pressure = healthySample() + pressure.sources['cloud-monitoring']!.signals['director.errors'] = signal(1) + pressure.sources['cloud-monitoring']!.signals['cloud_sql.cpu'] = signal(0.81) + expect(evaluateIncidentSample(pressure, startedAt).status).toBe('freeze') + }) + it('freezes on SQL, director, relay pool, heartbeat, and migration breaches', () => { const sample = healthySample() sample.sources['cloud-monitoring']!.signals['cloud_sql.cpu'] = signal(0.81) diff --git a/cloud/apps/relay-ops/src/incident-monitor.ts b/cloud/apps/relay-ops/src/incident-monitor.ts index 0887bb2d1ee..a48e100a6d8 100644 --- a/cloud/apps/relay-ops/src/incident-monitor.ts +++ b/cloud/apps/relay-ops/src/incident-monitor.ts @@ -101,7 +101,8 @@ export const INCIDENT_MONITOR_THRESHOLDS = { directorCpuUtilization: 0.8, directorMemoryUtilization: 0.8, directorConcurrency: 64, - directorErrors: 0, + // Sparse connection timeouts must not block a healthy rollout; four/5min still freezes. + directorErrors: 3, authErrors: 0, // Why: 800 exceeded the 600 hard cap, so this could never trigger on a capped cell. 500 is // the ordinary admission limit a cell actually stops at (600 cap - 100 control-rebind reserve). diff --git a/cloud/apps/relay/src/assignment-store.ts b/cloud/apps/relay/src/assignment-store.ts index 7b877e3af7d..bb7812dda86 100644 --- a/cloud/apps/relay/src/assignment-store.ts +++ b/cloud/apps/relay/src/assignment-store.ts @@ -1,3 +1,4 @@ +import { createDrainMigrationRowLookup } from './drain-migration-row-lookup.js' import { IDLE_REHOME_PAGE_SIZE, selectIdleRegionalRehomes } from './idle-regional-rehome-selection.js' import { readRegionCorrectionOutcomes } from './region-correction-outcomes.js' import { @@ -2187,22 +2188,16 @@ export class RelayAssignmentStore { [input.cellId] ) const cells = await this.lockCellInventory(transaction, 'request') + const assignmentRows = createDrainMigrationRowLookup(assignments, text) + const leaseRows = createDrainMigrationRowLookup(activityLeases, text) for (const migrationRow of migrations) { const identity = { userId: text(migrationRow, 'user_id'), relayHostId: text(migrationRow, 'relay_host_id') } - const assignment = assignments.find( - (candidate) => - text(candidate, 'user_id') === identity.userId && - text(candidate, 'relay_host_id') === identity.relayHostId - ) + const assignment = assignmentRows.first(identity) assertCurrentMigrationAssignment(assignment, migrationRow) - const leases = activityLeases.filter( - (lease) => - text(lease, 'user_id') === identity.userId && - text(lease, 'relay_host_id') === identity.relayHostId - ) + const leases = leaseRows.all(identity) const migrationLeases = leases.filter( (lease) => text(lease, 'activity_kind') === 'migration' ) @@ -2377,22 +2372,16 @@ export class RelayAssignmentStore { ) { throw new Error('drain_migration_source_incarnation_mismatch') } + const assignmentRows = createDrainMigrationRowLookup(assignments, text) + const leaseRows = createDrainMigrationRowLookup(activityLeases, text) for (const migrationRow of migrationIncarnations) { - const assignment = assignments.find( - (candidate) => - text(candidate, 'user_id') === text(migrationRow, 'user_id') && - text(candidate, 'relay_host_id') === text(migrationRow, 'relay_host_id') - ) + const identity = { + userId: text(migrationRow, 'user_id'), + relayHostId: text(migrationRow, 'relay_host_id') + } + const assignment = assignmentRows.first(identity) assertCurrentMigrationAssignment(assignment, migrationRow) - assertAssignmentActivityAccounting( - assignment, - activityLeases.filter( - (lease) => - text(lease, 'user_id') === text(migrationRow, 'user_id') && - text(lease, 'relay_host_id') === text(migrationRow, 'relay_host_id') - ), - migrationRow - ) + assertAssignmentActivityAccounting(assignment, leaseRows.all(identity), migrationRow) } const sendPermitExpiresAt = now + CELL_DRAIN_SEND_PERMIT_MS await transaction.query( diff --git a/cloud/apps/relay/src/drain-migration-row-lookup.test.ts b/cloud/apps/relay/src/drain-migration-row-lookup.test.ts new file mode 100644 index 00000000000..72df5224ee2 --- /dev/null +++ b/cloud/apps/relay/src/drain-migration-row-lookup.test.ts @@ -0,0 +1,62 @@ +import { expect, it } from 'vitest' +import { createDrainMigrationRowLookup } from './drain-migration-row-lookup.js' +import type { SqlRow } from './database.js' + +function text(row: SqlRow, field: string): string { + const value = row[field] + if (typeof value !== 'string') { + throw new Error(`invalid_${field}`) + } + return value +} + +it('keeps first assignment matches, lease order, row identity, and separate identity components', () => { + const rows = [ + { user_id: 'a:b', relay_host_id: 'c', value: 1 }, + { user_id: 'a', relay_host_id: 'b:c', value: 2 }, + { user_id: 'a:b', relay_host_id: 'c', value: 3 }, + { user_id: '', relay_host_id: '', value: 4 } + ] + const lookup = createDrainMigrationRowLookup(rows, text) + for (const identity of [ + { userId: 'a:b', relayHostId: 'c' }, + { userId: 'a', relayHostId: 'b:c' }, + { userId: '', relayHostId: '' }, + { userId: 'missing', relayHostId: 'c' } + ]) { + const expected = rows.filter( + (row) => + row.user_id === identity.userId && row.relay_host_id === identity.relayHostId + ) + expect(lookup.first(identity)).toBe(expected[0]) + expect(lookup.all(identity)).toEqual(expected) + lookup.all(identity).forEach((row, index) => expect(row).toBe(expected[index])) + } +}) + +it('retains lazy validation and short circuiting when an inventory is malformed', () => { + const first = { user_id: 'user', relay_host_id: 'host' } + const identity = { userId: 'user', relayHostId: 'host' } + const lookup = createDrainMigrationRowLookup( + [first, { user_id: null, relay_host_id: 'bad' }], + text + ) + expect(lookup.first(identity)).toBe(first) + expect(() => lookup.all(identity)).toThrow('invalid_user_id') + const unrelated = createDrainMigrationRowLookup( + [first, { user_id: 'other', relay_host_id: null }], + text + ) + expect(unrelated.all(identity)).toEqual([first]) + expect(() => unrelated.first({ userId: 'other', relayHostId: 'host' })).toThrow( + 'invalid_relay_host_id' + ) +}) + +it('does not share an index between refreshed inventories', () => { + const identity = { userId: 'user', relayHostId: 'host' } + const oldRow = { user_id: 'user', relay_host_id: 'host', version: 1 } + const newRow = { ...oldRow, version: 2 } + expect(createDrainMigrationRowLookup([oldRow], text).first(identity)).toBe(oldRow) + expect(createDrainMigrationRowLookup([newRow], text).first(identity)).toBe(newRow) +}) diff --git a/cloud/apps/relay/src/drain-migration-row-lookup.ts b/cloud/apps/relay/src/drain-migration-row-lookup.ts new file mode 100644 index 00000000000..b9dd5776f7d --- /dev/null +++ b/cloud/apps/relay/src/drain-migration-row-lookup.ts @@ -0,0 +1,58 @@ +import type { SqlRow } from './database.js' + +type Identity = { userId: string; relayHostId: string } +type RowIndex = Map> + +/** A single locked inventory, never retained across transactions or refreshed queries. */ +export function createDrainMigrationRowLookup( + rows: SqlRow[], + readText: (row: SqlRow, field: string) => string +): { + first: (identity: Identity) => SqlRow | undefined + all: (identity: Identity) => SqlRow[] +} { + let index: RowIndex | null | undefined + const indexed = (identity: Identity): SqlRow[] | undefined => { + if (index === undefined) { + index = indexRows(rows) + } + return index?.get(identity.userId)?.get(identity.relayHostId) + } + const matches = (row: SqlRow, identity: Identity): boolean => + readText(row, 'user_id') === identity.userId && + readText(row, 'relay_host_id') === identity.relayHostId + return { + first(identity) { + const group = indexed(identity) + return index === null ? rows.find((row) => matches(row, identity)) : group?.[0] + }, + all(identity) { + const group = indexed(identity) + return index === null ? rows.filter((row) => matches(row, identity)) : (group ?? []) + } + } +} + +function indexRows(rows: SqlRow[]): RowIndex | null { + const index: RowIndex = new Map() + for (const row of rows) { + const userId = row.user_id + const hostId = row.relay_host_id + // Preserve the original lazy validation and refusal order for malformed database rows. + if (typeof userId !== 'string' || typeof hostId !== 'string') { + return null + } + let hosts = index.get(userId) + if (!hosts) { + hosts = new Map() + index.set(userId, hosts) + } + const group = hosts.get(hostId) + if (group) { + group.push(row) + } else { + hosts.set(hostId, [row]) + } + } + return index +} diff --git a/cloud/apps/relay/src/drain-migration-row-scaling.test.ts b/cloud/apps/relay/src/drain-migration-row-scaling.test.ts new file mode 100644 index 00000000000..3ffd6490bd1 --- /dev/null +++ b/cloud/apps/relay/src/drain-migration-row-scaling.test.ts @@ -0,0 +1,121 @@ +import { ASSIGNMENT_LIMITS } from '@orca-cloud/relay-contract' +import { afterEach, expect, it } from 'vitest' +import { RelayAssignmentStore } from './assignment-store.js' +import { openInMemoryRelayDatabase, type RelayDatabase, type SqlRow } from './database.js' + +let database: RelayDatabase | undefined +afterEach(async () => await database?.close()) + +it.each([false, true])( + 'looks up a whole drain inventory with linear identity reads (expired: %s)', + async (expired) => { + database = await openInMemoryRelayDatabase() + let measuring = false + let identityReads = 0 + let indexedRows = 0 + const instrument = (delegate: RelayDatabase): RelayDatabase => ({ + query: (sql, params) => delegate.query(sql, params), + queryLocked: async (sql, params, options) => { + const rows = await delegate.queryLocked(sql, params, options) + if ( + !measuring || + !sql.includes('WHERE EXISTS') || + !(sql.includes('SELECT assignment.*') || sql.includes('SELECT lease.*')) + ) { + return rows + } + indexedRows += rows.length + return rows.map( + (row): SqlRow => + new Proxy(row, { + get(target, key) { + if (key === 'user_id' || key === 'relay_host_id') { + identityReads++ + } + return Reflect.get(target, key) + } + }) + ) + }, + transaction: (operation, options) => + delegate.transaction((tx) => operation(instrument(tx)), options), + close: () => delegate.close() + }) + let now = 100 + const store = new RelayAssignmentStore(instrument(database), () => now, { + requireLiveCells: true + }) + const cells = ['a', 'b'].map((id) => ({ + id: `cell-${id}`, + url: `https://relay-${id}.example.com`, + capacityRequests: 500 + })) + await store.reconcileCells(cells) + const incarnation = '11111111-1111-4111-8111-111111111111' + for (const cell of cells) { + await store.recordCellHeartbeat({ + cellId: cell.id, + cellUrl: cell.url, + cellIncarnation: incarnation, + startedAt: 50, + ready: true, + observedRequests: 0 + }) + } + await store.setCellEnabled('cell-b', false) + const identities = Array.from({ length: 50 }, (_, index) => ({ + userId: `user-${index % 5}`, + relayHostId: `host${String(index).padStart(12, '0')}` + })) + for (const identity of identities) { + await store.assign(identity) + } + await store.setCellEnabled('cell-b', true) + await store.setCellEnabled('cell-a', false) + for (const identity of identities) { + const migration = await store.startEvacuation(identity, 'cell-b') + await store.markMigrationTargetRegistered(identity, { + cellId: 'cell-b', + assignmentEpoch: migration.assignmentEpoch + }) + } + const attempt = { + attemptId: '55555555-5555-4555-8555-555555555555', + cellId: 'cell-a', + cellIncarnation: incarnation, + traceValue: '66666666-6666-4666-8666-666666666666', + plannedGraceMs: 120_000 + } + await store.prepareCellDrainAttempt(attempt) + if (expired) { + now += ASSIGNMENT_LIMITS.migrationLeaseMs + 1 + await store.releaseExpiredActivityLeases() + for (const cell of cells) { + await store.recordCellHeartbeat({ + cellId: cell.id, + cellUrl: cell.url, + cellIncarnation: incarnation, + startedAt: 50, + ready: true, + observedRequests: 0 + }) + } + } + measuring = true + await expect(store.beginCellDrainSend(attempt)).resolves.toMatchObject({ + state: 'send-may-have-started', + shouldSend: true + }) + expect(indexedRows).toBeGreaterThanOrEqual(100) + expect(identityReads).toBeLessThanOrEqual(indexedRows * 2) + const migrations = await database.query( + 'SELECT expires_at FROM relay_assignment_migrations' + ) + expect(migrations).toHaveLength(50) + expect( + migrations.every( + (row) => row.expires_at === now + ASSIGNMENT_LIMITS.migrationLeaseMs + ) + ).toBe(true) + } +) diff --git a/cloud/apps/relay/src/host-session-client-accept.test.ts b/cloud/apps/relay/src/host-session-client-accept.test.ts index 04f86c533e4..0b7febaa7b4 100644 --- a/cloud/apps/relay/src/host-session-client-accept.test.ts +++ b/cloud/apps/relay/src/host-session-client-accept.test.ts @@ -424,6 +424,8 @@ describe('successful client accept timing', () => { ) as { connId: string; connTicket: string } // The desktop's data leg is the attach window this is meant to expose. now += 23 + const session = h.registry.get({ userId: identity.sub, relayHostId: identity.relayHostId })! + const ownerProbe = vi.spyOn(session.pendingConns, 'has') const accepted = await h.registry.acceptHostData( hostData as unknown as WebSocket, connOpen.connId, @@ -432,6 +434,7 @@ describe('successful client accept timing', () => { ) expect(accepted).toBe(true) + expect(ownerProbe).toHaveBeenCalledOnce() expect(h.observer.recordClientAcceptCompleted).toHaveBeenCalledWith({ totalMs: 49, stageMs: { assignment: 5, credential: 7, activity: 11, attach: 23, basis: 3 } @@ -485,6 +488,80 @@ async function advanceToPing(control: FakeSocket, clock: { now: number }): Promi return (JSON.parse(String(ping[0])) as { t: number }).t } +// The attach resolves its owning session once and hands it to the unfenced leg; +// these hold the session it must be and the order the client hears about it. +describe('host data attach ownership', () => { + beforeEach(() => vi.useFakeTimers()) + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + }) + + const bystander = { ...identity, sub: 'user-2', relayHostId: 'qponmlkjihgfedcb' } + + async function pendingAttach(h: ReturnType) { + const client = new FakeSocket() + await h.registry.acceptClient( + client as unknown as WebSocket, + identity.relayHostId, + 'credential' + ) + const session = h.registry.get({ userId: identity.sub, relayHostId: identity.relayHostId })! + return { client, session, pending: [...session.pendingConns.values()][0]! } + } + + it('attaches the session that owns the connection, not the first one registered', async () => { + const h = harness() + const idle = new FakeSocket() + await h.activate(idle as unknown as WebSocket, bystander, null, 1, false, 1, '1.4.197') + await activeHost(h) + const { client, session, pending } = await pendingAttach(h) + const idleSession = h.registry.get({ + userId: bystander.sub, + relayHostId: bystander.relayHostId + })! + const host = new FakeSocket() + expect( + await h.registry.acceptHostData( + host as unknown as WebSocket, + pending.connId, + pending.connTicket, + 1 + ) + ).toBe(true) + expect(client.send).toHaveBeenCalledWith(expect.stringContaining('"type":"relay-hello"')) + expect(session.activeSplices.has(pending.connId)).toBe(true) + expect(idleSession.activeSplices.size).toBe(0) + expect(idleSession.activeConnIds.size).toBe(0) + h.registry.drain(0) + vi.advanceTimersByTime(0) + }) + + it('acknowledges the client only after the connection basis is persisted', async () => { + const h = harness() + await activeHost(h) + const { client, session, pending } = await pendingAttach(h) + const basis = deferred() + h.store.recordConnectionBasis.mockReturnValueOnce(basis.promise) + const host = new FakeSocket() + const attaching = h.registry.acceptHostData( + host as unknown as WebSocket, + pending.connId, + pending.connTicket, + 1 + ) + await vi.advanceTimersByTimeAsync(0) + expect(h.store.recordConnectionBasis).toHaveBeenCalledOnce() + expect(client.send).not.toHaveBeenCalledWith(expect.stringContaining('relay-hello')) + basis.resolve() + expect(await attaching).toBe(true) + expect(client.send).toHaveBeenCalledWith(expect.stringContaining('"type":"relay-hello"')) + expect(session.activeSplices.has(pending.connId)).toBe(true) + h.registry.drain(0) + vi.advanceTimersByTime(0) + }) +}) + describe('control round-trip sampling', () => { beforeEach(() => vi.useFakeTimers()) afterEach(() => { diff --git a/cloud/apps/relay/src/host-session-registry.test.ts b/cloud/apps/relay/src/host-session-registry.test.ts index dcfcd3f36c5..7c50285454c 100644 --- a/cloud/apps/relay/src/host-session-registry.test.ts +++ b/cloud/apps/relay/src/host-session-registry.test.ts @@ -111,6 +111,7 @@ function createRegistry( renewControlActivity: ReturnType releaseActivity: ReturnType observer: { + recordAuth: ReturnType recordControlClose: ReturnType recordSpliceClose: ReturnType } @@ -1396,6 +1397,57 @@ describe('source-owned idle cutover', () => { cleanup.resolve() await attached }) + it('rejects an attach mid-cutover before its ticket is ever examined', async () => { + const h = await source({ failReservation: vi.fn().mockResolvedValue(undefined) }) + const result = deferred<{ outcome: 'deferred' }>() + // The cutover must already be in flight: an idle host is what it claims. + const moving = h.registry.idleRehome(request, () => result.promise, vi.fn()) + const client = new FakeSocket() + h.session.pendingConns.set('conn', { + connId: 'conn', + connTicket: 'ticket', + client: client as unknown as WebSocket, + reservation: { + userId: identity.sub, + relayHostId: identity.relayHostId, + credentialKind: 'invite', + leaseExpiresAt: Date.now() + 1000 + }, + attachTimer: setTimeout(() => {}, 1000), + credentialActivityId: null + } as never) + const host = new FakeSocket() + // The ticket below is the live one: only the cutover fence may reject it. + expect( + await h.registry.acceptHostData(host as unknown as WebSocket, 'conn', 'ticket', 1) + ).toBe(false) + expect(host.close).toHaveBeenCalledWith(RELAY_CLOSE_CODE.WRONG_CELL, expect.any(String)) + expect(h.observer.recordAuth).not.toHaveBeenCalled() + expect(h.session.pendingConns.has('conn')).toBe(true) + expect(h.session.activeConnIds.size).toBe(0) + result.resolve({ outcome: 'deferred' }) + await moving + }) + it('holds no attach ownership when no session owns the connection', async () => { + const h = await source() + const host = new FakeSocket() + expect( + await h.registry.acceptHostData(host as unknown as WebSocket, 'stranger', 'ticket', 1) + ).toBe(false) + expect(h.observer.recordAuth).toHaveBeenCalledWith(false) + expect(host.close).toHaveBeenCalledWith( + RELAY_CLOSE_CODE.BAD_OUTER_CREDENTIAL, + expect.any(String) + ) + // A leaked idle-work hold from the unowned attach would report `busy` here. + expect( + await h.registry.idleRehome( + request, + vi.fn().mockResolvedValue({ outcome: 'committed' }), + vi.fn() + ) + ).toEqual({ outcome: 'committed' }) + }) it('returns the durable operation outcome after source retirement', async () => { const h = await source() const commit = vi.fn().mockResolvedValue({ outcome: 'committed' }) @@ -1421,3 +1473,226 @@ describe('source-owned idle cutover', () => { expect(h.registry.get(request)).toBeNull() }) }) + +// The host data leg's owner lookup is the registry's only whole-inventory scan on +// an attach. These count what that scan touches, not how long it takes. +describe('host data attach owner lookup', () => { + beforeEach(() => vi.useFakeTimers()) + afterEach(() => { + vi.clearAllTimers() + vi.useRealTimers() + }) + + const SESSION_COUNT = 1000 + const CONN_ID = 'conn-owned' + const OWNER_INDEX = { first: 0, middle: 499, last: 999 } as const + type Placement = keyof typeof OWNER_INDEX | 'absent' + type LookupCounts = { visits: number; membership: number } + + function bindOwn(map: Map, property: string | symbol): unknown { + const value: unknown = Reflect.get(map, property, map) + return typeof value === 'function' ? value.bind(map) : value + } + + // One visit per session the scan pulls off the map iterator; answers unchanged. + function countingValues(map: Map, counts: LookupCounts): Map { + return new Proxy(map, { + get(target, property) { + if (property !== 'values') return bindOwn(target, property) + return function* (): Generator { + for (const value of target.values()) { + counts.visits += 1 + yield value + } + } + } + }) + } + + // One membership check per `pendingConns.has`; answers unchanged. + function countingHas(map: Map, counts: LookupCounts): Map { + return new Proxy(map, { + get(target, property) { + if (property !== 'has') return bindOwn(target, property) + return (key: K) => { + counts.membership += 1 + return target.has(key) + } + } + }) + } + + // The pre-change implementation, kept inline as the oracle the new counts are + // differenced against: two inventory arrays, two independent finds. + function legacyOwnerLookup( + sessions: Map, + connId: string + ): { owner: HostSession | undefined; session: HostSession | undefined } { + const owner = [...sessions.values()].find((candidate) => candidate.pendingConns.has(connId)) + const session = [...sessions.values()].find((candidate) => candidate.pendingConns.has(connId)) + return { owner, session } + } + + function pendingConn(client: FakeSocket, connTicket: string) { + return { + connId: CONN_ID, + connTicket, + client: client as unknown as WebSocket, + reservation: { + userId: identity.sub, + relayHostId: identity.relayHostId, + credentialKind: 'invite', + leaseExpiresAt: Date.now() + 1000 + }, + attachTimer: setTimeout(() => {}, 1000), + credentialActivityId: null + } as never + } + + // Every decoy holds a pending conn of its own, so each membership check the + // scan makes is real work rather than a lookup in an empty map. + function decoySession(index: number, counts: LookupCounts): HostSession { + const pendingConns = new Map([[`conn-decoy-${index}`, { connId: 'decoy' }]]) + return { + relayHostId: `decoy-host-${index}`, + generation: 1, + state: 'active', + activeConnIds: new Set(), + pendingConns: countingHas(pendingConns, counts) + } as unknown as HostSession + } + + async function attachRegistry(placement: Placement, store: Partial = {}) { + const h = createRegistry(vi.fn().mockResolvedValue('control:1'), { + failReservation: vi.fn().mockResolvedValue(undefined), + recordConnectionBasis: vi.fn().mockResolvedValue(undefined), + deactivateBasis: vi.fn().mockResolvedValue(undefined), + ...store + }) + const control = new FakeSocket() + await h.activate(control as unknown as WebSocket, identity, null, 1, false, 1) + const internals = h.registry as unknown as { sessions: Map } + const [ownerKey, owner] = [...internals.sessions.entries()][0]! + const counts: LookupCounts = { visits: 0, membership: 0 } + const client = new FakeSocket() + if (placement !== 'absent') owner.pendingConns.set(CONN_ID, pendingConn(client, 'ticket')) + owner.pendingConns = countingHas(owner.pendingConns, counts) + const ordered: HostSession[] = [] + const sessions = new Map() + const ownerIndex = placement === 'absent' ? 0 : OWNER_INDEX[placement] + for (let index = 0; index < SESSION_COUNT; index += 1) { + const session = index === ownerIndex ? owner : decoySession(index, counts) + ordered.push(session) + sessions.set(index === ownerIndex ? ownerKey : `decoy-${index}`, session) + } + internals.sessions = countingValues(sessions, counts) + return { ...h, owner, ordered, counts, client, control, sessions: internals.sessions } + } + + it.each([ + { + placement: 'first', + before: { visits: 2000, membership: 2 }, + after: { visits: 1, membership: 1 } + }, + { + placement: 'middle', + before: { visits: 2000, membership: 1000 }, + after: { visits: 500, membership: 500 } + }, + { + placement: 'last', + before: { visits: 2000, membership: 2000 }, + after: { visits: 1000, membership: 1000 } + }, + { + placement: 'absent', + before: { visits: 2000, membership: 2000 }, + after: { visits: 1000, membership: 1000 } + } + ] as const)( + 'visits the inventory once, not twice, for a $placement owner', + async ({ placement, before, after }) => { + const h = await attachRegistry(placement) + expect(h.sessions.size).toBe(SESSION_COUNT) + const oracle = legacyOwnerLookup(h.sessions, CONN_ID) + const legacy = { ...h.counts } + h.counts.visits = 0 + h.counts.membership = 0 + const host = new FakeSocket() + // An unusable ticket stops the attach immediately after the lookup, so the + // counts below belong to the lookup alone. + expect( + await h.registry.acceptHostData(host as unknown as WebSocket, CONN_ID, 'wrong', 1) + ).toBe(false) + expect(h.observer.recordAuth).toHaveBeenCalledExactlyOnceWith(false) + expect(host.close).toHaveBeenCalledWith( + RELAY_CLOSE_CODE.BAD_OUTER_CREDENTIAL, + 'invalid host data ticket' + ) + expect(legacy).toEqual(before) + expect({ ...h.counts }).toEqual(after) + expect(oracle.owner).toBe(placement === 'absent' ? undefined : h.owner) + expect(oracle.owner).toBe(oracle.session) + } + ) + + it.each([ + { reason: 'ticket', ticket: 'wrong', generation: 1, state: 'active' }, + { reason: 'generation', ticket: 'ticket', generation: 2, state: 'active' }, + { reason: 'state', ticket: 'ticket', generation: 1, state: 'orphaned' } + ] as const)('fails an attach whose $reason does not match the owner', async (input) => { + const h = await attachRegistry('middle') + h.owner.state = input.state + const host = new FakeSocket() + expect( + await h.registry.acceptHostData( + host as unknown as WebSocket, + CONN_ID, + input.ticket, + input.generation + ) + ).toBe(false) + expect(h.observer.recordAuth).toHaveBeenCalledExactlyOnceWith(false) + expect(host.close).toHaveBeenCalledWith( + RELAY_CLOSE_CODE.BAD_OUTER_CREDENTIAL, + 'invalid host data ticket' + ) + expect(h.owner.pendingConns.has(CONN_ID)).toBe(true) + expect(h.owner.activeConnIds.size).toBe(0) + }) + + it('rejects on the earlier duplicate owner rather than the later live one', async () => { + const h = await attachRegistry('middle') + h.ordered[0]!.pendingConns.set(CONN_ID, pendingConn(new FakeSocket(), 'stale-ticket') as never) + expect(legacyOwnerLookup(h.sessions, CONN_ID).owner).toBe(h.ordered[0]) + h.counts.visits = 0 + h.counts.membership = 0 + const host = new FakeSocket() + expect( + await h.registry.acceptHostData(host as unknown as WebSocket, CONN_ID, 'ticket', 1) + ).toBe(false) + expect({ ...h.counts }).toEqual({ visits: 1, membership: 1 }) + expect(h.owner.pendingConns.has(CONN_ID)).toBe(true) + }) + + it('splices the earlier duplicate owner and leaves the later one untouched', async () => { + const basis = vi.fn().mockRejectedValue(new Error('basis failed')) + const h = await attachRegistry('first', { recordConnectionBasis: basis }) + const duplicate = h.ordered[3]! + duplicate.pendingConns.set(CONN_ID, pendingConn(new FakeSocket(), 'ticket') as never) + h.counts.visits = 0 + h.counts.membership = 0 + const host = new FakeSocket() + expect( + await h.registry.acceptHostData(host as unknown as WebSocket, CONN_ID, 'ticket', 1) + ).toBe(false) + expect({ ...h.counts }).toEqual({ visits: 1, membership: 1 }) + expect(h.observer.recordAuth).toHaveBeenCalledWith(true) + expect(basis).toHaveBeenCalledOnce() + // The first owner's entry was consumed; the later duplicate never was. + expect(h.owner.pendingConns.has(CONN_ID)).toBe(false) + expect(duplicate.pendingConns.has(CONN_ID)).toBe(true) + expect(h.owner.activeConnIds.size).toBe(0) + }) +}) diff --git a/cloud/apps/relay/src/host-session-registry.ts b/cloud/apps/relay/src/host-session-registry.ts index 61a1b706fa9..0d0cf4a7940 100644 --- a/cloud/apps/relay/src/host-session-registry.ts +++ b/cloud/apps/relay/src/host-session-registry.ts @@ -511,16 +511,22 @@ export class HostSessionRegistry { connTicket: string, generation: number ): Promise { - const owner = [...this.sessions.values()].find((candidate) => - candidate.pendingConns.has(connId) - ) + // First insertion-order owner, and the only scan the attach makes: the + // unfenced leg reuses this result instead of repeating the search. + let owner: HostSession | undefined + for (const candidate of this.sessions.values()) { + if (candidate.pendingConns.has(connId)) { + owner = candidate + break + } + } const release = owner ? this.beginIdleWork(owner.relayHostId) : () => {} if (!release) { socket.close(RELAY_CLOSE_CODE.WRONG_CELL, 'idle cutover in progress') return false } try { - return await this.acceptHostDataUnfenced(socket, connId, connTicket, generation) + return await this.acceptHostDataUnfenced(socket, connId, connTicket, generation, owner) } finally { release() } @@ -530,11 +536,9 @@ export class HostSessionRegistry { socket: WebSocket, connId: string, connTicket: string, - generation: number + generation: number, + session: HostSession | undefined ): Promise { - const session = [...this.sessions.values()].find((candidate) => - candidate.pendingConns.has(connId) - ) const pending = session?.pendingConns.get(connId) if ( !session || diff --git a/cloud/apps/relay/src/relay-observability.test.ts b/cloud/apps/relay/src/relay-observability.test.ts index ea6734412be..605aacf6422 100644 --- a/cloud/apps/relay/src/relay-observability.test.ts +++ b/cloud/apps/relay/src/relay-observability.test.ts @@ -5,6 +5,7 @@ import { observeRelayDatabase } from './observed-relay-database.js' import { CONTROL_RTT_RESERVOIR_LIMIT, observedRelayRequests, + percentile, RelayObservability, type RelayProcessCounts } from './relay-observability.js' @@ -412,3 +413,181 @@ describe('relay observability', () => { expect(recordSql.mock.calls.map((call) => call[1])).toEqual([true, false, true, false]) }) }) + +// The pre-change implementation, kept verbatim as the differential oracle. Both +// ranks sorted their own copy and the maximum was a zero-seeded fold. +function legacyPercentile(values: number[], percentileRank: number): number { + if (values.length === 0) return 0 + const sorted = [...values].sort((left, right) => left - right) + return sorted[Math.ceil(percentileRank * sorted.length) - 1] ?? 0 +} + +function legacyLatencySummary(samples: number[]): { p50: number; p95: number; max: number } { + const round = (value: number): number => Number(value.toFixed(3)) + return { + p50: round(legacyPercentile(samples, 0.5)), + p95: round(legacyPercentile(samples, 0.95)), + max: round(samples.reduce((highest, sample) => Math.max(highest, sample), 0)) + } +} + +// `-0` and `NaN` both survive a string round trip, unlike a bare equality check. +function describeNumber(value: number): string { + return Object.is(value, -0) ? '-0' : String(value) +} + +function expectSameNumber(actual: number, expected: number, label: string): void { + expect(`${label} = ${describeNumber(actual)}`).toBe(`${label} = ${describeNumber(expected)}`) +} + +function sparseWindow(size: number, filled: Record): number[] { + const values: number[] = new Array(size) + for (const [index, value] of Object.entries(filled)) values[Number(index)] = value + return values +} + +// Lehmer generator: stays inside the safe-integer range so the window is +// byte-identical on every engine the relay runs on. +function deterministicWindow(size: number): number[] { + let seed = 20_260_912 + return Array.from({ length: size }, () => { + seed = (seed * 48_271) % 2_147_483_647 + return (seed % 4_000_000) / 1_000 + }) +} + +const DENSE_WINDOWS: Array<{ name: string; values: number[] }> = [ + { name: 'empty', values: [] }, + { name: 'single', values: [7.5] }, + { name: 'single negative', values: [-7.5] }, + { name: 'ascending', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }, + { name: 'descending', values: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] }, + { name: 'duplicates', values: [4, 4, 4, 4, 4] }, + // The trap: a sorted last element reads -1 here, the zero-seeded fold reads 0. + { name: 'all negative', values: [-5, -1, -9, -3, -2] }, + { name: 'mixed signs', values: [-2, 3, -7, 0, 11, -0.5] }, + { name: 'signed zero', values: [0, -0, -0, 0] }, + { name: 'negative then signed zero', values: [-3, -0, -1] }, + { name: 'nan leading', values: [NaN, 5, 1, 9] }, + { name: 'nan trailing', values: [5, 1, 9, NaN] }, + { name: 'nan interleaved', values: [5, NaN, 1, NaN, 9] }, + { name: 'all nan', values: [NaN, NaN, NaN] }, + { name: 'positive infinity', values: [Infinity, 3, 1] }, + { name: 'negative infinity', values: [-Infinity, -3, -1] }, + { name: 'both infinities', values: [Infinity, -Infinity, 3, -Infinity] }, + { name: 'infinities and nan', values: [Infinity, NaN, -Infinity, 0] }, + { name: 'sub-millisecond rounding', values: [0.00049, 0.0005, 0.00051, 0.9995] }, + { name: 'reservoir sized', values: deterministicWindow(CONTROL_RTT_RESERVOIR_LIMIT) } +] + +// Holes cannot reach the recorders, so they are exercised through `percentile` +// alone — the surface `host-session-registry` also calls. +const SPARSE_WINDOWS: Array<{ name: string; values: number[] }> = [ + { name: 'all holes', values: sparseWindow(4, {}) }, + { name: 'leading hole', values: sparseWindow(5, { 3: 8, 4: 2 }) }, + { name: 'trailing hole', values: sparseWindow(5, { 0: 8, 1: 2 }) }, + { name: 'interleaved holes', values: sparseWindow(6, { 0: 3, 2: -4, 5: 1 }) }, + { name: 'holes with nan', values: sparseWindow(5, { 1: NaN, 3: 6 }) } +] + +const PERCENTILE_RANKS = [0, 0.05, 0.5, 0.9, 0.95, 0.99, 1] + +type SortWork = { sorts: number; comparisons: number; copiedElements: number } + +// Every sorted array here is a fresh spread copy, so its length is the number of +// elements copied to produce it. +function countSortWork(run: () => void): SortWork { + const work: SortWork = { sorts: 0, comparisons: 0, copiedElements: 0 } + const original = Array.prototype.sort + const patched = Array.prototype as { sort: unknown } + patched.sort = function (this: T[], compare?: (left: T, right: T) => number): T[] { + work.sorts++ + work.copiedElements += this.length + return original.call(this, (left: T, right: T) => { + work.comparisons++ + return compare ? compare(left, right) : String(left) < String(right) ? -1 : 1 + }) + } + try { + run() + } finally { + patched.sort = original + } + return work +} + +function summaryThroughFlush(samples: number[]): { p50: number; p95: number; max: number } { + const entries: Array> = [] + const observability = new RelayObservability( + { role: 'cell', cellId: 'staging-c1', region: 'us-central1' }, + (entry) => entries.push(entry) + ) + for (const sample of samples) observability.recordControlRenewal(sample, 'renewed') + observability.flush(counts) + const entry = entries[0]! + return { + p50: entry.controlRenewalLatencyMsP50 as number, + p95: entry.controlRenewalLatencyMsP95 as number, + max: entry.controlRenewalLatencyMsMax as number + } +} + +describe('latency window summarisation', () => { + it('matches the pre-change percentile on every edge-case window', () => { + let compared = 0 + for (const { name, values } of [...DENSE_WINDOWS, ...SPARSE_WINDOWS]) { + for (const rank of PERCENTILE_RANKS) { + expectSameNumber( + percentile(values, rank), + legacyPercentile(values, rank), + `${name} @ p${rank}` + ) + compared++ + } + } + expect(compared).toBe((DENSE_WINDOWS.length + SPARSE_WINDOWS.length) * PERCENTILE_RANKS.length) + }) + + it('matches the pre-change p50, p95 and maximum through a flush', () => { + let compared = 0 + for (const { name, values } of DENSE_WINDOWS) { + const actual = summaryThroughFlush(values) + const expected = legacyLatencySummary(values) + expectSameNumber(actual.p50, expected.p50, `${name} p50`) + expectSameNumber(actual.p95, expected.p95, `${name} p95`) + // The zero-seeded fold, not the sorted last element: all-negative and NaN + // windows disagree between the two. + expectSameNumber(actual.max, expected.max, `${name} max`) + compared += 3 + } + expect(compared).toBe(DENSE_WINDOWS.length * 3) + // The trap, spelled out: the sorted window ends at -1 but the fold reports 0. + expect(summaryThroughFlush([-5, -1, -9, -3, -2]).max).toBe(0) + expect(Number.isNaN(summaryThroughFlush([5, NaN, 1]).max)).toBe(true) + }) + + it('sorts each latency window once instead of once per rank', () => { + const samples = deterministicWindow(CONTROL_RTT_RESERVOIR_LIMIT) + const before = countSortWork(() => legacyLatencySummary(samples)) + const after = countSortWork(() => summaryThroughFlush(samples)) + + expect(before.sorts).toBe(2) + expect(after.sorts).toBe(1) + expect(before.copiedElements).toBe(2 * CONTROL_RTT_RESERVOIR_LIMIT) + expect(after.copiedElements).toBe(CONTROL_RTT_RESERVOIR_LIMIT) + // Identical input and comparator, so the dropped sort is exactly half the + // comparator calls rather than an engine-specific constant. + expect(before.comparisons).toBeGreaterThan(CONTROL_RTT_RESERVOIR_LIMIT) + expect(after.comparisons).toBe(before.comparisons / 2) + }) + + it('never sorts an empty window and leaves the caller window untouched', () => { + const samples = [5, -1, NaN, 3, -0] + const before = samples.map(describeNumber) + expect(countSortWork(() => summaryThroughFlush([])).sorts).toBe(0) + expect(countSortWork(() => percentile([], 0.95)).sorts).toBe(0) + countSortWork(() => summaryThroughFlush(samples)) + percentile(samples, 0.5) + expect(samples.map(describeNumber)).toEqual(before) + }) +}) diff --git a/cloud/apps/relay/src/relay-observability.ts b/cloud/apps/relay/src/relay-observability.ts index 5e85758ca56..f5eabb60a4c 100644 --- a/cloud/apps/relay/src/relay-observability.ts +++ b/cloud/apps/relay/src/relay-observability.ts @@ -168,10 +168,18 @@ const emptyDeltas = (): RelayMetricDeltas => ({ controlActivityRecoveryFailures: 0 }) +function ascending(values: number[]): number[] { + return [...values].sort((left, right) => left - right) +} + +// Holes and NaN land past the requested rank, so the fallback still applies. +function nearestRank(sorted: number[], percentileRank: number): number { + return sorted[Math.ceil(percentileRank * sorted.length) - 1] ?? 0 +} + export function percentile(values: number[], percentileRank: number): number { if (values.length === 0) return 0 - const sorted = [...values].sort((left, right) => left - right) - return sorted[Math.ceil(percentileRank * sorted.length) - 1] ?? 0 + return nearestRank(ascending(values), percentileRank) } function roundMs(value: number): number { @@ -179,11 +187,15 @@ function roundMs(value: number): number { } // Spreading a window into Math.max blows the stack once a busy cell samples -// enough of it, so the maximum is folded instead. +// enough of it, so the maximum is folded instead. The fold is also not +// interchangeable with the sorted last element: it is seeded with zero, so an +// all-negative or NaN window reads differently. function latencySummary(samples: number[]): { p50: number; p95: number; max: number } { + // One sorted copy serves both ranks. + const sorted = samples.length === 0 ? samples : ascending(samples) return { - p50: roundMs(percentile(samples, 0.5)), - p95: roundMs(percentile(samples, 0.95)), + p50: roundMs(nearestRank(sorted, 0.5)), + p95: roundMs(nearestRank(sorted, 0.95)), max: roundMs(samples.reduce((highest, sample) => Math.max(highest, sample), 0)) } } diff --git a/cloud/dev/scripts/probe-relay-rehome-trust.mjs b/cloud/dev/scripts/probe-relay-rehome-trust.mjs index 2208131e58a..73ea44a40f7 100644 --- a/cloud/dev/scripts/probe-relay-rehome-trust.mjs +++ b/cloud/dev/scripts/probe-relay-rehome-trust.mjs @@ -38,7 +38,7 @@ export function parseRehomeTrustProbeArguments(argv, environment = process.env) export async function probeRehomeTrust(config, dependencies = {}) { const fetchImpl = dependencies.fetch ?? fetch - const response = await fetchAdminOnceMore( + const request = () => fetchAdminOnceMore( fetchImpl, `${config.directorOrigin}/v1/admin/regional-rehome-trust-probe`, { @@ -55,9 +55,26 @@ export async function probeRehomeTrust(config, dependencies = {}) { }, { wait: dependencies.wait } ) - const body = await response.json().catch(() => ({})) + let response = await request() + let body = await response.json().catch(() => ({})) + // The director wraps source HTTP failures in 409; retry only explicit transient statuses. + if (response.status === 409 && /^regional_rehome_trust_probe_source_(500|502|503|504)$/.test(body?.error ?? '')) { + await (dependencies.wait ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))))(2_000) + response = await request() + body = await response.json().catch(() => ({})) + } if (!response.ok) { - throw new Error(`application-mediated rehome trust probe returned ${response.status}`) + const safeReasons = new Set([ + 'invalid_token', 'director_only', 'invalid_request', + 'regional_rehome_trust_not_configured', + 'regional_rehome_trust_probe_source_unavailable', + 'regional_rehome_trust_probe_source_invalid_response', + 'regional_rehome_trust_probe_not_proven', + ...[400, 401, 403, 404, 409, 429, 500, 502, 503, 504] + .map((status) => `regional_rehome_trust_probe_source_${status}`) + ]) + const reason = safeReasons.has(body?.error) ? body.error : 'unrecognized_error' + throw new Error(`application-mediated rehome trust probe returned ${response.status}: ${reason}`) } if ( body.v !== 1 || diff --git a/cloud/dev/scripts/probe-relay-rehome-trust.test.mjs b/cloud/dev/scripts/probe-relay-rehome-trust.test.mjs index 789d33c40b6..034fcd06c4f 100644 --- a/cloud/dev/scripts/probe-relay-rehome-trust.test.mjs +++ b/cloud/dev/scripts/probe-relay-rehome-trust.test.mjs @@ -131,3 +131,40 @@ test('approves the asia-east2 rehome sources and still rejects unlisted cells', ) } }) + +test('retries one director-wrapped source 503 without relaxing the proof', async () => { + let calls = 0 + const result = await probeRehomeTrust(parseRehomeTrustProbeArguments(argv, environment), { + wait: async () => {}, + fetch: async () => ++calls === 1 + ? Response.json({ error: 'regional_rehome_trust_probe_source_503' }, { status: 409 }) + : Response.json(provenProbe) + }) + assert.equal(calls, 2) + assert.equal(result.proven, true) +}) + +test('reports safe trust reasons, keeps rejection final, and redacts arbitrary error text', async () => { + for (const reason of ['regional_rehome_trust_probe_source_403', 'secret-token-example']) { + let calls = 0 + await assert.rejects(probeRehomeTrust(parseRehomeTrustProbeArguments(argv, environment), { + wait: async () => { throw new Error('must not retry') }, + fetch: async () => { calls++; return Response.json({ error: reason }, { status: 409 }) } + }), error => { + assert.match(error.message, /returned 409/) + assert.ok(!error.message.includes('secret-token-example')) + if (reason.endsWith('_403')) assert.match(error.message, /source_403/) + return true + }) + assert.equal(calls, 1) + } +}) + +test('stops after the second wrapped transient failure', async () => { + let calls = 0 + await assert.rejects(probeRehomeTrust(parseRehomeTrustProbeArguments(argv, environment), { + wait: async () => {}, + fetch: async () => { calls++; return Response.json({ error: 'regional_rehome_trust_probe_source_503' }, { status: 409 }) } + }), /returned 409.*source_503/) + assert.equal(calls, 2) +}) diff --git a/cloud/dev/scripts/relay-production-same-cap-wave.mjs b/cloud/dev/scripts/relay-production-same-cap-wave.mjs index 6e84c1c9104..8caa68e7ff2 100644 --- a/cloud/dev/scripts/relay-production-same-cap-wave.mjs +++ b/cloud/dev/scripts/relay-production-same-cap-wave.mjs @@ -87,18 +87,21 @@ export function canaryAuthority(input) { } export function verifyCanaryAuthority(authority, expected, repositoryRoot) { + const selectorGeneration = Number(expected.selectorGeneration) if ( authority?.v !== 1 || !/^[0-9a-f]{40}$/.test(authority.commitSha ?? '') || authority.runId !== expected.runId || authority.targetDigest !== expected.targetDigest || authority.rollbackDigest !== expected.rollbackDigest || - authority.selectorGeneration !== Number(expected.selectorGeneration) || + !Number.isSafeInteger(authority.selectorGeneration) || + authority.selectorGeneration < 0 || + !Number.isSafeInteger(selectorGeneration) || + selectorGeneration < authority.selectorGeneration || authority.rehomeGeneration !== Number(expected.rehomeGeneration) || !SAME_CAP_CELLS.includes(authority.cellId) ) throw new Error('canary authority does not match this batch') - // The batch dispatch resolves main after the canary sealed, so bind to the same code, not the - // same SHA; every field above still pins this batch to that exact canary. + // Each cell checks exact live selector state; later batches may reuse this control epoch's canary. requireSameEvidenceCode({ sealedSha: authority.commitSha, currentSha: expected.commitSha, diff --git a/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs b/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs index d636c324b33..7377f7a7af3 100644 --- a/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs +++ b/cloud/dev/scripts/relay-production-same-cap-wave.test.mjs @@ -109,6 +109,41 @@ test('seals and verifies canary authority for later batches', () => { }), /does not match/) }) +test('reuses a canary across selector advances only within the same control epoch', () => { + const authority = canaryAuthority({ + cellIds: 'production-gce-c7', targetDigest, rollbackDigest, + confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} production-gce-c7`, + commitSha: 'c'.repeat(40), runId: '42', selectorGeneration: '11', rehomeGeneration: '4' + }) + const expected = { + commitSha: 'c'.repeat(40), runId: '42', targetDigest, rollbackDigest, + selectorGeneration: '21', rehomeGeneration: '4' + } + for (const generation of ['13', '14', '21', '29']) { + assert.equal(verifyCanaryAuthority(authority, { + ...expected, selectorGeneration: generation + }), authority) + } + for (const generation of ['12', '-1', 'NaN', 'Infinity', '13.5', '9007199254740992']) { + assert.throws(() => verifyCanaryAuthority(authority, { + ...expected, selectorGeneration: generation + }), /does not match/) + } + for (const generation of [-1, NaN, Infinity, 13.5, '13', Number.MAX_SAFE_INTEGER + 1]) { + assert.throws(() => verifyCanaryAuthority({ + ...authority, selectorGeneration: generation + }, expected), /does not match/) + } + for (const mismatch of [ + { rehomeGeneration: '3' }, { rehomeGeneration: '5' }, + { targetDigest: rollbackDigest }, { rollbackDigest: targetDigest }, { runId: '43' } + ]) { + assert.throws(() => verifyCanaryAuthority(authority, { + ...expected, ...mismatch + }), /does not match/) + } +}) + function gitIn(root, ...args) { return execFileSync('git', ['-C', root, ...args], { encoding: 'utf8' }).trim() } @@ -158,7 +193,7 @@ test('a batch trusts a canary sealed by identical code at an ancestor commit', a runId: '42', targetDigest, rollbackDigest, - selectorGeneration: '13', + selectorGeneration: '21', rehomeGeneration: '4' }, repositoryRoot) assert.equal(verifyAt(repository.sameCode, repository.root).cellId, 'production-gce-c7') diff --git a/cloud/docs/relay-incident-monitor.md b/cloud/docs/relay-incident-monitor.md index 696efb85296..a97e92949e8 100644 --- a/cloud/docs/relay-incident-monitor.md +++ b/cloud/docs/relay-incident-monitor.md @@ -112,7 +112,8 @@ durably marked consumed before mutation and cannot authorize another run. | Director instances | outside 5–6 | | Director CPU or memory | over 80% | | Director concurrency | over 64 | -| Unexpected director 5xx or auth 5xx in five minutes | over 0 | +| Unexpected director 5xx in five minutes (excludes 503) | over 3 | +| Auth 5xx in five minutes | over 0 | | Connections per cell process | over 500 | | Queued bytes per cell process | over 48 MiB | | Blocked or expired/unregistered migration | over 0 | @@ -276,3 +277,7 @@ without its segment is a compile error in relay-contract, not a silent gap. load the director's three-connection database pool. - Added private atomic state, idempotent JSONL checkpoints, and secret-safe Markdown evidence. - Added the manual production workflow. It has not been dispatched. + +### Director error allowance (2026-09-12) + +The serving-cell rollout observed three unexpected director 500 responses among approximately 33,600 responses in an hour, all two-second PostgreSQL connection timeouts. CPU remained near 30–37% and the zero-error bar repeatedly prevented any cell mutation. The five-minute allowance is now three non-503 director 5xx; four freezes. Auth errors, data freshness, active probes, SQL/pool pressure and other limits are unchanged. This is a bounded operational allowance, not a calibrated SLO or proof that intermittent failures are resolved; persistent low-frequency errors below this limit still require diagnosis. diff --git a/config/packaged-runtime-node-modules.cjs b/config/packaged-runtime-node-modules.cjs index 1ee443f8288..a491ae23ac8 100644 --- a/config/packaged-runtime-node-modules.cjs +++ b/config/packaged-runtime-node-modules.cjs @@ -35,7 +35,7 @@ const PACKAGED_RUNTIME_PACKAGE_ROOTS = [ ] const WINDOWS_PACKAGED_RUNTIME_PACKAGE_ROOTS = [ '@vscode/windows-process-tree', - 'windows-native-registry' + '@orca/windows-registry' ] const NODE_PTY_PREBUILD_PREFIX_BY_PLATFORM = { diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 5b21fa9b0ad..bd402ad883c 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "updatedAt": "2026-08-31", + "updatedAt": "2026-09-11", "policy": { "maturityLevels": ["experimental", "soak", "blocking", "accepted-gap", "deprecated"], "blockingPromotion": { @@ -10,6 +10,78 @@ } }, "gates": [ + { + "id": "agent-session.journal-streaming-replay", + "title": "Journal replay bounds obsolete revision memory without changing recovery", + "maturity": "experimental", + "protection": "partial", + "owner": "agent-session-runtime", + "layer": "runtime-unit", + "surfaces": ["structured chat journal replay", "structured chat recovery"], + "platforms": ["macos", "linux", "windows"], + "providers": ["local", "remote-runtime"], + "coveredPlatforms": ["macos"], + "coveredProviders": ["local"], + "coverageNotes": "Production SQLite and reducer tests on macOS. Execution-host-local storage behavior is shared by remote runtimes; no live SSH or Windows/Linux run. PTY, daemon, WSL process launch, transport framing and mobile rendering are unaffected.", + "motivatingLinks": [ + "https://github.com/stablyai/orca/blob/main/src/main/native-chat/agent-session-journal/journal-open.ts" + ], + "invariant": "Replay preserves latest revisions, original item order, fences, aliases, submissions, repair precedence, read-only schema latching and cursor cleanup while retaining live items rather than all historical bodies.", + "oracle": "Replay 2,048 16 KiB revisions into one latest item with less than 8 MiB sampled live heap growth; preserve prefix and future-schema latching after a gap, malformed suffix repair precedence, and hold no SQLite read snapshot across reduction (a mid-replay checkpoint is not busy). Existing journal and subscriber tests cover replayed content and recovery.", + "commands": [ + "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/native-chat/agent-session-journal src/main/native-chat/agent-session-wire/agent-session-history-page.test.ts src/main/native-chat/agent-session-wire/agent-session-history-forward-read-budget.test.ts src/main/native-chat/agent-session-wire/structured-agent-session-subscribers.test.ts src/main/native-chat/agent-session-wire/agent-session-journal-recovery.test.ts", + "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/native-chat/agent-session-journal/journal-streaming-replay.test.ts src/main/native-chat/agent-session-journal/journal-corruption-repair.test.ts" + ], + "testFiles": [ + "src/main/native-chat/agent-session-journal/journal-streaming-replay.test.ts", + "src/main/native-chat/agent-session-journal/journal-corruption-repair.test.ts" + ], + "assertionRefs": [ + { + "file": "src/main/native-chat/agent-session-journal/journal-streaming-replay.test.ts", + "assertions": [ + "releases superseded revision bodies while reducing a long journal", + "holds no read snapshot while reducing, so a checkpoint can pass mid-replay", + "keeps the prefix but latches read-only for a future row beyond a gap", + "keeps gap repair precedence when a later row is malformed", + "rejects an unanchored prefix before a later gap" + ] + } + ], + "evidenceRuns": [ + { + "date": "2026-09-11", + "runner": "local", + "platform": "macos", + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/native-chat/agent-session-journal src/main/native-chat/agent-session-wire/agent-session-history-page.test.ts src/main/native-chat/agent-session-wire/agent-session-history-forward-read-budget.test.ts src/main/native-chat/agent-session-wire/structured-agent-session-subscribers.test.ts src/main/native-chat/agent-session-wire/agent-session-journal-recovery.test.ts", + "result": "passed", + "durationSeconds": 7.71, + "summary": "245 tests passed across 22 files. Retained-heap oracle fails on baseline at 68.6 MB and passes under 8 MiB with streaming; gap/schema and cursor-cleanup assertions passed." + } + ], + "runtimeBudget": { + "p95Seconds": 30, + "scope": "Unit fixtures; p95 not established." + }, + "flakeHistory": { + "status": "not-started", + "evidence": "Local candidate validation only; no CI soak." + }, + "redGreenEvidence": { + "status": "complete", + "evidence": "Production-function AB/BA benchmark samples 132.6 MB live heap in baseline vs 88-92 KB with streaming on a 66.7 MB revision-heavy journal. The retained-heap unit test fails against the original code and passes with streaming; value and cursor assertions pass in both implementations." + }, + "performanceBudget": { + "required": true, + "evidence": "Paged SQLite reads (one completed statement per page) and immediate reduction retain reduced state plus one page of rows. No cursor or read snapshot outlives its statement, so a WAL checkpoint can pass mid-replay; no new polling, subprocess, provider call or wire change." + }, + "knownGaps": [ + "No Windows/Linux runtime execution or real remote-host validation.", + "Latest live message bodies still require memory proportional to their total size; this removes superseded-history retention, not live-history storage." + ], + "promotionCriteria": ["Retain red/green heap and value assertions and complete CI soak."], + "demotionRule": "Keep experimental until cross-platform and soak evidence; investigate failures without weakening content or memory assertions." + }, { "id": "runtime.connection-owned-host-status", "title": "Host status recovers with its owning connection", @@ -15544,6 +15616,93 @@ ], "demotionRule": "Cannot promote without metric artifacts and stable p95 runtime history." }, + { + "id": "terminal-performance.daemon-ndjson-wire-parity-and-serialization", + "title": "Daemon NDJSON preserves wire bytes within a serialization count budget", + "maturity": "experimental", + "protection": "partial", + "owner": "terminal-performance", + "layer": "daemon-provider-contract", + "surfaces": ["daemon stream", "NDJSON framing", "stream data batching"], + "platforms": ["macos", "linux", "windows"], + "providers": ["daemon", "ssh", "remote-runtime"], + "coveredPlatforms": ["macos"], + "coveredProviders": ["daemon"], + "coverageNotes": "Deterministic writer, batcher, droppability and NDJSON suites run locally on macOS with mocked socket/process boundaries. An SSH-shaped session ID is a string fixture, not SSH transport evidence. No live daemon, platform integration or mixed-version client/host pair is exercised; folder and git workspaces are not distinguished by these stream contracts.", + "motivatingLinks": ["src/main/daemon/daemon-stream-data-split.ts"], + "invariant": "Reusing an encoded unsplit metadata-free frame must preserve the previous writer's exact wire bytes and chunk boundaries while reducing that path to one encode; oversized and metadata-bearing writes retain existing semantics and transformed writes remain uncapped single frames.", + "oracle": "Compare emitted lines to the previous writer algorithm across byte caps, escaped and Unicode payloads, session IDs, raw lengths, sequence numbers and transformed spans; assert exact newline framing, reconstruct split payloads, check surrogate boundaries and sequence spans, and count encodeNdjson calls.", + "commands": [ + "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/daemon/daemon-stream-data-split.test.ts src/main/daemon/daemon-stream-data-batcher.test.ts src/main/daemon/daemon-stream-droppable-membership.test.ts src/main/daemon/daemon-stream-droppability-lifecycle.test.ts src/main/daemon/ndjson.test.ts" + ], + "testFiles": [ + "src/main/daemon/daemon-stream-data-split.test.ts", + "src/main/daemon/daemon-stream-data-batcher.test.ts", + "src/main/daemon/daemon-stream-droppable-membership.test.ts", + "src/main/daemon/daemon-stream-droppability-lifecycle.test.ts", + "src/main/daemon/ndjson.test.ts" + ], + "assertionRefs": [ + { + "file": "src/main/daemon/daemon-stream-data-split.test.ts", + "assertions": [ + "encodes an unsplit metadata-free frame once: %j", + "reuses the encoded frame exactly at the inclusive byte cap", + "does not add a duplicate full-data sizing probe to oversized writes", + "keeps transformed writes at one encode without applying the ordinary byte cap", + "preserves exact frames, chunk boundaries and metadata across payloads and caps", + "keeps JSON escaping, Unicode and newline framing byte-for-byte", + "keeps split frames within the byte cap and preserves code points and sequence spans" + ] + }, + { + "file": "src/main/daemon/daemon-stream-data-batcher.test.ts", + "assertions": ["writes large stream data as parser-sized NDJSON events"] + }, + { + "file": "src/main/daemon/ndjson.test.ts", + "assertions": ["measures multibyte payloads in UTF-8 bytes, not characters"] + } + ], + "evidenceRuns": [ + { + "date": "2026-09-11", + "runner": "local", + "platform": "macos", + "command": "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/daemon/daemon-stream-data-split.test.ts src/main/daemon/daemon-stream-data-batcher.test.ts src/main/daemon/daemon-stream-droppable-membership.test.ts src/main/daemon/daemon-stream-droppability-lifecycle.test.ts src/main/daemon/ndjson.test.ts", + "result": "passed", + "summary": "Five daemon suites passed: 70 tests total, including the new 12-test splitter suite. Vitest reported 14.76 seconds; no app or live transport validation was performed.", + "durationSeconds": 14.76 + } + ], + "runtimeBudget": { + "p95Seconds": 60, + "scope": "Target budget for the five deterministic suites; one local Vitest run took 14.76 seconds, not an established p95. Native-runtime setup is excluded from the reported Vitest duration." + }, + "flakeHistory": { + "status": "not-started", + "evidence": "Fresh local five-suite run passed; no sustained CI soak history is established." + }, + "redGreenEvidence": { + "status": "partial", + "evidence": "Historical report states serialization-count tests failed against the old writer. That baseline run was not repeated for this registration; the fresh candidate run passed all 70 tests. The previous-writer oracle checks byte parity, not live cross-version compatibility." + }, + "performanceBudget": { + "required": true, + "evidence": "Unsplit metadata-free frames, including the inclusive byte-cap boundary, require exactly one encodeNdjson call; oversized writes must not exceed the previous writer's encode count; transformed writes require one encode. These are deterministic call-count budgets, not measured throughput, CPU, heap or input-latency improvements." + }, + "promotionCriteria": [ + "Capture reproducible old-writer red and candidate green artifacts for the serialization-count assertions.", + "Collect CI soak evidence before promotion and validate Linux/Windows runtimes and live SSH/remote and mixed-version pairs before claiming those integrations." + ], + "knownGaps": [ + "No live daemon/Electron, Linux, Windows, WSL, SSH, remote-runtime or mixed-version client/host evidence.", + "The historical red run has not been independently reproduced for this registration; the parity oracle reuses current splitter/encoder helpers.", + "No sustained soak, measured p95, wall-clock performance benchmark or native socket backpressure guarantee.", + "Byte-cap assertions cover ordinary split frames at a viable cap; tiny caps and transformed frames retain legacy behavior rather than gaining a universal cap guarantee." + ], + "demotionRule": "Keep experimental until reproducible red/green and soak evidence exist; do not relax exact wire-byte, chunk-boundary or encode-count assertions to hide regressions." + }, { "id": "terminal-performance.daemon-stream-backpressure", "title": "Daemon terminal streams respect socket backpressure under output floods", diff --git a/config/scripts/agent-lineage-cycle-cleanup-benchmark.mjs b/config/scripts/agent-lineage-cycle-cleanup-benchmark.mjs new file mode 100644 index 00000000000..47407764e0f --- /dev/null +++ b/config/scripts/agent-lineage-cycle-cleanup-benchmark.mjs @@ -0,0 +1,90 @@ +// Run: node --expose-gc config/scripts/agent-lineage-cycle-cleanup-benchmark.mjs [baseline-ref] +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFile } from 'node:fs/promises' +import { performance } from 'node:perf_hooks' +import { transform } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const sourcePath = 'src/renderer/src/components/dashboard/agent-row-lineage-model.ts' +const baseline = process.argv[2] ?? '20ab9950654' +const beforeSource = execFileSync('git', ['show', `${baseline}:${sourcePath}`], { + encoding: 'utf8', + windowsHide: true +}) +async function load(source) { + const { code } = await transform(source, { loader: 'ts', format: 'esm' }) + return (await import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`)) + .buildAgentRowLineageTree +} +const before = await load(beforeSource) +const after = await load(await readFile(sourcePath, 'utf8')) + +function row(index, parent) { + return { + paneKey: `pane-${index}`, + entry: { + terminalHandle: `term-${index}`, + orchestration: parent === undefined ? undefined : { parentPaneKey: `pane-${parent}` } + } + } +} + +// Exercise duplicate keys, disconnected cycles, missing parents, and handle fallback. +let seed = 7391 +function random(max) { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return seed % max +} +for (let sample = 0; sample < 500; sample++) { + const rows = Array.from({ length: 40 }, () => { + const value = row(random(30), random(40)) + value.entry.orchestration.parentTerminalHandle = `term-${random(40)}` + value.entry.orchestration.coordinatorHandle = `term-${random(40)}` + return value + }) + if (sample % 2 === 0) { + rows.unshift(row('root', undefined)) + } + assert.deepEqual(after(rows), before(rows)) +} + +const results = [] +for (const [shape, count] of [ + ['flat', 1000], + ['all-cycles', 1000], + ['mixed-cycles', 100], + ['mixed-cycles', 500], + ['mixed-cycles', 1000] +]) { + const rows = Array.from({ length: count }, (_, index) => + row(index, shape === 'flat' ? undefined : index ^ 1) + ) + if (shape === 'mixed-cycles') { + rows.unshift(row('root', undefined)) + } + assert.deepEqual(after(rows), before(rows)) + for (let warmup = 0; warmup < 30; warmup++) { + before(rows) + after(rows) + } + const samples = { before: [], after: [] } + for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) { + for (const arm of pair) { + global.gc?.() + const run = arm === 'before' ? before : after + const cpu = process.cpuUsage() + const start = performance.now() + for (let iteration = 0; iteration < 30; iteration++) { + run(rows) + } + const wallMs = (performance.now() - start) / 30 + const used = process.cpuUsage(cpu) + samples[arm].push({ wallMs, cpuMs: (used.user + used.system) / 30_000 }) + } + } + results.push({ shape, count, samples }) +} +console.log( + JSON.stringify({ baseline, node: process.version, parityGraphs: 500, results }, null, 2) +) diff --git a/config/scripts/agent-lineage-reachability-benchmark.mjs b/config/scripts/agent-lineage-reachability-benchmark.mjs new file mode 100644 index 00000000000..4122ac18dab --- /dev/null +++ b/config/scripts/agent-lineage-reachability-benchmark.mjs @@ -0,0 +1,114 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { performance } from 'node:perf_hooks' +import { transform } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' +import { summarizeBenchmarkSamples } from './benchmark-sample-summary.mjs' + +// git show :src/renderer/src/components/dashboard/agent-row-lineage-model.ts | node config/scripts/agent-lineage-reachability-benchmark.mjs +async function load(source) { + const { code } = await transform(source, { loader: 'ts', format: 'esm' }) + return (await import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`)) + .buildAgentRowLineageTree +} +const implementations = { + before: await load(readFileSync(0, 'utf8')), + after: await load( + readFileSync('src/renderer/src/components/dashboard/agent-row-lineage-model.ts', 'utf8') + ) +} +function orderedTree(tree) { + return { + roots: tree.rootRows, + children: [...tree.childrenByParentPaneKey], + childKeys: [...tree.childPaneKeys] + } +} + +let seed = 42 +const random = (max) => { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return Math.floor((seed / 2 ** 32) * max) +} +let differentialCases = 0 +for (let trial = 0; trial < 5000; trial += 1) { + const count = random(100) + const rows = Object.freeze( + Array.from({ length: count }, (_, index) => + Object.freeze({ + paneKey: `pane-${random(count + 4)}`, + index, + entry: Object.freeze({ + terminalHandle: random(2) ? `term-${random(count)}` : undefined, + orchestration: Object.freeze({ + parentPaneKey: random(3) ? `pane-${random(count + 4)}` : undefined, + parentTerminalHandle: random(2) ? `term-${random(count)}` : undefined, + coordinatorHandle: random(2) ? `term-${random(count)}` : undefined + }) + }) + }) + ) + ) + assert.deepEqual( + orderedTree(implementations.after(rows)), + orderedTree(implementations.before(rows)) + ) + differentialCases += 1 +} + +const results = [] +for (const count of [8, 32, 128, 512, 1024]) { + for (const shape of ['flat', 'fanout', 'balanced', 'chain']) { + const rows = Array.from({ length: count }, (_, index) => { + const parent = + shape === 'fanout' ? 0 : shape === 'balanced' ? Math.floor((index - 1) / 4) : index - 1 + return { + paneKey: `pane-${index}`, + entry: { + orchestration: + index > 0 && shape !== 'flat' ? { parentPaneKey: `pane-${parent}` } : undefined + } + } + }) + const expected = orderedTree(implementations.before(rows)) + assert.deepEqual(orderedTree(implementations.after(rows)), expected) + const iterations = Math.max(5, Math.floor(10_000 / count)) + for (let warmup = 0; warmup < 20; warmup += 1) { + implementations.before(rows) + implementations.after(rows) + } + /** @type {{ before: number[], after: number[] }} */ + const samples = { before: [], after: [] } + for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) { + for (const arm of pair) { + let result + const started = performance.now() + for (let repeat = 0; repeat < iterations; repeat += 1) { + result = implementations[arm](rows) + } + samples[arm].push(performance.now() - started) + assert.deepEqual(orderedTree(result), expected) + } + } + results.push({ + count, + shape, + iterations, + meanMicrosecondsPerTree: Object.fromEntries( + Object.entries(samples).map(([arm, values]) => [ + arm, + (values.reduce((sum, ms) => sum + ms, 0) * 1000) / values.length / iterations + ]) + ), + before: summarizeBenchmarkSamples(samples.before), + after: summarizeBenchmarkSamples(samples.after) + }) + } +} +console.log( + JSON.stringify( + { node: process.version, platform: process.platform, differentialCases, results }, + null, + 2 + ) +) diff --git a/config/scripts/check-job-log-byte-cap-benchmark.mjs b/config/scripts/check-job-log-byte-cap-benchmark.mjs new file mode 100644 index 00000000000..3a8fd530919 --- /dev/null +++ b/config/scripts/check-job-log-byte-cap-benchmark.mjs @@ -0,0 +1,162 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import path from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' + +// Pipe the baseline check-job-log-tail-slice.ts on stdin; both arms use the actual UTF-8 implementation. +const entry = path.resolve('src/shared/check-job-log-tail-slice.ts') +const sources = [readFileSync(0, 'utf8'), readFileSync(entry, 'utf8')] +assert(sources.every((source) => source.includes('export function sliceCheckLogTail'))) + +async function load(source) { + const result = await build({ + entryPoints: [entry], + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: [ + { + name: 'log-excerpt-source', + setup(builder) { + builder.onLoad({ filter: /check-job-log-tail-slice\.ts$/ }, () => ({ + contents: source, + loader: 'ts', + resolveDir: path.dirname(entry) + })) + } + } + ] + }) + const bundled = `${result.outputFiles[0].text}\n//# sourceURL=check-log-byte-cap-benchmark-bundle.js` + return import(`data:text/javascript;base64,${Buffer.from(bundled).toString('base64')}`) +} + +const modules = await Promise.all(sources.map(load)) +const arms = modules.map((module) => module.sliceCheckLogTail) +const limit = modules[0].PR_CHECK_LOG_TAIL_BYTES +assert.equal(modules[1].PR_CHECK_LOG_TAIL_BYTES, limit) +let seed = 0xc0ffee16 +function random(max) { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return (seed >>> 8) % max +} + +let comparisons = 0 +function compare(text) { + const expected = arms[0](text) + assert.equal(arms[1](text), expected) + assert(Buffer.byteLength(expected, 'utf8') <= limit) + comparisons++ + return expected +} + +const units = ['x', 'é', '界', '😀', '\ud83d', '\udc00', 'x\ud83d界\udc00'] +for (const unit of units) { + for (let delta = -4; delta <= 4; delta++) { + const text = unit.repeat(Math.floor(limit / Buffer.byteLength(unit)) + delta) + compare(text) + compare(`error: ${text}\n${'recent\n'.repeat(103)}`) + } +} +const endings = ['\n', '\r\n', '\r', ''] +const tokens = [ + 'plain text', + '##[error]', + '::error::', + 'error:', + 'FAILED', + 'exit code', + 'ENOENT', + 'EACCES', + 'panic:', + 'AssertionError', + 'emoji 😀', + '\ud83d', + '\udc00', + '\0', + '界', + 'é', + '\r' +] +for (let iteration = 0; iteration < 5000; iteration++) { + const rows = Array.from({ length: random(250) }, (_, index) => { + const token = tokens[random(tokens.length)] + if (index === 0 && iteration % 20 === 0) { + return `${token}${units[random(units.length)].repeat(limit + random(4))}` + } + return `${token} ${index} ${units[random(units.length)].repeat(random(30))}` + }) + compare(rows.join(endings[random(endings.length)]) + endings[random(endings.length)]) +} +console.log(`${comparisons} full-output differential cases passed`) + +const workloads = [ + ['short ASCII', 'log '.repeat(16)], + ['short Unicode', '🦀界'.repeat(20)], + ['8KiB ASCII', 'x'.repeat(8192)], + ['16KiB ASCII exact cap', 'x'.repeat(limit)], + ['8Ki code units / 24KiB Unicode', '界'.repeat(8192)], + ['2MiB ASCII line', 'x'.repeat(2 * 1024 * 1024)], + ['8MiB ASCII line', 'x'.repeat(8 * 1024 * 1024)], + ['2MiB Unicode line', '界'.repeat(Math.floor((2 * 1024 * 1024) / 3))], + [ + '2MiB earlier error context', + `error: ${'x'.repeat(2 * 1024 * 1024)}\n${'recent\n'.repeat(103)}` + ], + [ + '220 ordinary lines', + Array.from({ length: 220 }, (_, i) => `line ${i} ${'text'.repeat(8)}`).join('\n') + ], + [ + '220 lines / small earlier error', + Array.from( + { length: 220 }, + (_, i) => `${i === 30 ? 'error:' : 'line'} ${i} ${'text'.repeat(8)}` + ).join('\n') + ] +] + +function sample(arm, input, expected, repeats) { + const started = performance.now() + let output + for (let i = 0; i < repeats; i++) { + output = arm(input) + } + const elapsed = (performance.now() - started) / repeats + assert.equal(output, expected) + return elapsed +} + +console.log( + JSON.stringify({ + node: process.version, + platform: process.platform, + arch: process.arch, + pairs: 8, + unit: 'ms' + }) +) +for (const [name, input] of workloads) { + const expected = compare(input) + for (const arm of arms) { + const until = performance.now() + 80 + while (performance.now() < until) { + sample(arm, input, expected, 1) + } + } + const repeats = Math.max(1, Math.min(100000, Math.ceil(40 / sample(arms[0], input, expected, 1)))) + /** @type {number[][]} */ + const samples = [[], []] + for (let pair = 0; pair < 8; pair++) { + for (const index of pair % 2 ? [1, 0] : [0, 1]) { + samples[index].push(sample(arms[index], input, expected, repeats)) + } + } + const median = samples.map((values) => { + values.sort((a, b) => a - b) + return (values[3] + values[4]) / 2 + }) + console.log(JSON.stringify({ name, repeats, median, samples })) +} diff --git a/config/scripts/check-readme-local-links.mjs b/config/scripts/check-readme-local-links.mjs new file mode 100644 index 00000000000..723d7e3ccce --- /dev/null +++ b/config/scripts/check-readme-local-links.mjs @@ -0,0 +1,102 @@ +import { execFileSync } from 'node:child_process' +import { readdirSync, readFileSync } from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import { pathToFileURL } from 'node:url' + +// Why: the READMEs embed media from trees other jobs own (docs-site public media, +// generated feature-wall tiles), and the docs-only classifier skips the whole CI +// matrix for one of them. GitHub renders only committed files, so this checks the +// git index rather than the working tree. +const TRANSLATED_README_DIR = path.join('docs', 'readme') +const EXTERNAL_TARGET = /^(?:[a-z][a-z0-9+.-]*:|#|\/\/)/i +const HTML_ATTRIBUTE = /\b(?:src|srcset|href)\s*=\s*(?:"([^"]*)"|'([^']*)')/g +const MARKDOWN_LINK = /!?\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g + +function readmeFiles(root) { + const translated = readdirSync(path.join(root, TRANSLATED_README_DIR)) + .filter((name) => name.endsWith('.md')) + .sort() + .map((name) => path.posix.join('docs', 'readme', name)) + return ['README.md', ...translated] +} + +// Why only the referenced paths: a full `git ls-files` of this repo overflows the +// default child buffer; asking about a few dozen pathspecs stays bounded. +function trackedFiles(root, candidates) { + if (candidates.length === 0) { + return new Set() + } + const stdout = execFileSync( + 'git', + ['--literal-pathspecs', 'ls-files', '-z', '--', ...candidates], + { cwd: root, encoding: 'utf8' } + ) + return new Set(stdout.split('\0').filter(Boolean)) +} + +function* localTargets(markdown) { + for (const match of markdown.matchAll(HTML_ATTRIBUTE)) { + // Why: srcset is a candidate list ("a.gif 1x, b.gif 2x"); each entry starts with a URL. + for (const candidate of (match[1] ?? match[2]).split(',')) { + const target = candidate.trim().split(/\s+/)[0] + if (target) { + yield target + } + } + } + for (const match of markdown.matchAll(MARKDOWN_LINK)) { + yield match[1].replace(/^<|>$/g, '') + } +} + +function resolveTarget(readme, target) { + const bare = target.split(/[?#]/)[0] + if (!bare) { + return null + } + const resolved = path.posix.normalize( + path.posix.join(path.posix.dirname(readme), decodeURIComponent(bare)) + ) + return resolved.startsWith('../') ? null : resolved +} + +function collectLinks(root) { + const links = [] + for (const readme of readmeFiles(root)) { + const markdown = readFileSync(path.join(root, readme), 'utf8') + for (const target of new Set(localTargets(markdown))) { + if (EXTERNAL_TARGET.test(target)) { + continue + } + links.push({ readme, target, resolved: resolveTarget(readme, target) }) + } + } + return links +} + +export function findBrokenReadmeLinks(root) { + const links = collectLinks(root) + const candidates = [...new Set(links.map((link) => link.resolved).filter(Boolean))] + const tracked = trackedFiles(root, candidates) + return links.filter(({ resolved }) => resolved === null || !tracked.has(resolved)) +} + +export function main(root = process.cwd()) { + const broken = findBrokenReadmeLinks(root) + if (broken.length > 0) { + console.error(`README local link check failed with ${broken.length} broken link(s):`) + for (const { readme, target, resolved } of broken) { + console.error( + `- ${readme}: ${target} -> ${resolved ?? 'outside the repository'} is not tracked` + ) + } + return 1 + } + console.log('README local link check passed.') + return 0 +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + process.exit(main()) +} diff --git a/config/scripts/check-readme-local-links.test.mjs b/config/scripts/check-readme-local-links.test.mjs new file mode 100644 index 00000000000..2f7cfc76c92 --- /dev/null +++ b/config/scripts/check-readme-local-links.test.mjs @@ -0,0 +1,156 @@ +import { execFileSync } from 'node:child_process' +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import path from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { parse } from 'yaml' +import { findBrokenReadmeLinks, main } from './check-readme-local-links.mjs' + +const projectDir = path.resolve(import.meta.dirname, '../..') +const tempDirs = [] + +function git(cwd, args) { + return execFileSync('git', args, { cwd, encoding: 'utf8' }).trim() +} + +function writeFiles(root, files) { + for (const [relativePath, contents] of Object.entries(files)) { + const target = path.join(root, relativePath) + mkdirSync(path.dirname(target), { recursive: true }) + writeFileSync(target, contents) + } +} + +function makeFixture(files, { untracked = {} } = {}) { + const root = mkdtempSync(path.join(tmpdir(), 'orca-readme-links-')) + tempDirs.push(root) + git(root, ['init', '--quiet']) + git(root, ['config', 'user.email', 'readme-links-test@example.com']) + git(root, ['config', 'user.name', 'README Links Test']) + writeFiles(root, files) + git(root, ['add', '-A']) + git(root, ['commit', '--quiet', '-m', 'fixture']) + writeFiles(root, untracked) + return root +} + +const validReadmes = { + 'README.md': [ + '', + '', + '日本語', + "", + '', + '[Contributing](.github/CONTRIBUTING.md) [Docs](https://example.com/docs) [Top](#top)', + '![hero](docs/assets/hero%20image.jpg "Hero")' + ].join('\n'), + 'docs/readme/README.ja.md': [ + '', + '', + 'English self', + '[LICENSE](../../LICENSE)' + ].join('\n'), + 'resources/build/icon.png': 'png', + 'resources/onboarding/feature-wall/tile-01.poster.jpg': 'jpg', + 'docs/site/public/docs/tab-split.gif': 'gif', + 'docs/assets/hero image.jpg': 'jpg', + '.github/CONTRIBUTING.md': 'contributing', + LICENSE: 'mit' +} + +afterEach(() => { + vi.restoreAllMocks() + while (tempDirs.length > 0) { + rmSync(tempDirs.pop(), { force: true, recursive: true }) + } +}) + +describe('README local link check', () => { + it('accepts the checked-in READMEs', () => { + vi.spyOn(console, 'log').mockImplementation(() => {}) + expect(main(projectDir)).toBe(0) + }) + + it('accepts local links in every supported shape', () => { + expect(findBrokenReadmeLinks(makeFixture(validReadmes))).toEqual([]) + }) + + it('reports a deleted media file for the root and translated READMEs', () => { + const { 'docs/site/public/docs/tab-split.gif': _gif, ...files } = validReadmes + vi.spyOn(console, 'error').mockImplementation(() => {}) + const root = makeFixture(files) + + expect(findBrokenReadmeLinks(root)).toEqual([ + { + readme: 'README.md', + target: 'docs/site/public/docs/tab-split.gif', + resolved: 'docs/site/public/docs/tab-split.gif' + }, + { + readme: 'docs/readme/README.ja.md', + target: '../site/public/docs/tab-split.gif', + resolved: 'docs/site/public/docs/tab-split.gif' + } + ]) + expect(main(root)).toBe(1) + expect(console.error).toHaveBeenCalledWith( + expect.stringContaining('README.md: docs/site/public/docs/tab-split.gif') + ) + }) + + // Why: GitHub renders the commit, so a file that only exists on disk is broken. + it('reports a referenced file that exists on disk but is not tracked', () => { + const { 'resources/build/icon.png': icon, ...files } = validReadmes + const root = makeFixture(files, { untracked: { 'resources/build/icon.png': icon } }) + + expect(findBrokenReadmeLinks(root).map((link) => link.resolved)).toEqual([ + 'resources/build/icon.png', + 'resources/build/icon.png' + ]) + }) + + it('reports a link that escapes the repository', () => { + const root = makeFixture({ + ...validReadmes, + 'docs/readme/README.ja.md': '' + }) + + expect(findBrokenReadmeLinks(root)).toEqual([ + { readme: 'docs/readme/README.ja.md', target: '../../../outside.png', resolved: null } + ]) + }) + + // Why: a single-quoted attribute is valid HTML and GitHub renders it, so a parser + // that only reads double quotes would pass a README with a broken image. + it('reports a missing target in a single-quoted attribute', () => { + const files = { + ...validReadmes, + 'README.md': `${validReadmes['README.md']}\n` + } + + expect(findBrokenReadmeLinks(makeFixture(files))).toEqual([ + { + readme: 'README.md', + target: 'docs/assets/missing.gif', + resolved: 'docs/assets/missing.gif' + } + ]) + }) + + // Why the ungated job: static_analysis is skipped for docs-only diffs, which is + // exactly the kind of PR that deletes a docs-site GIF the README embeds. + it('runs on every PR through the ungated guard job and in the lint script', () => { + const { scripts } = JSON.parse(readFileSync(path.join(projectDir, 'package.json'), 'utf8')) + const workflow = parse(readFileSync(path.join(projectDir, '.github/workflows/pr.yml'), 'utf8')) + const guardJob = workflow.jobs.root_directory_guard + const step = guardJob.steps.find((candidate) => candidate.name === 'Check README local links') + + expect(guardJob.if).toBeUndefined() + expect(guardJob.needs).toBeUndefined() + expect(step.run).toBe('node config/scripts/check-readme-local-links.mjs') + expect(scripts['check:readme-local-links']).toBe( + 'node config/scripts/check-readme-local-links.mjs' + ) + expect(scripts.lint).toContain('pnpm run check:readme-local-links') + }) +}) diff --git a/config/scripts/ci-dependency-download-cache.test.mjs b/config/scripts/ci-dependency-download-cache.test.mjs new file mode 100644 index 00000000000..9cce5bb0f6f --- /dev/null +++ b/config/scripts/ci-dependency-download-cache.test.mjs @@ -0,0 +1,30 @@ +import { readFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' +import { parse } from 'yaml' + +const read = (path) => parse(readFileSync(path, 'utf8')) +const workflow = (name) => read(`.github/workflows/${name}.yml`) +const action = read('.github/actions/install-node-dependencies/action.yml') + +describe('CI dependency download caches', () => { + it('scopes desktop stores to the root lockfile and lets mixed installs opt in', () => { + expect(action.inputs['cache-dependency-path'].default).toBe('pnpm-lock.yaml') + for (const step of action.runs.steps.filter((step) => step.uses === 'actions/setup-node@v6')) { + expect(step.with.cache).toBe('pnpm') + expect(step.with['cache-dependency-path']).toBe('${{ inputs.cache-dependency-path }}') + } + const install = action.runs.steps.find((step) => step.name === 'Install dependencies') + expect(install.if).toBeUndefined() + expect(install.run).toContain('pnpm install --frozen-lockfile --ignore-scripts') + expect(install.run).toContain( + 'diff --exit-code -- package.json pnpm-lock.yaml pnpm-workspace.yaml' + ) + const mobile = workflow('mobile').jobs.verify.steps.find((step) => + step.uses?.includes('install-node-dependencies') + ) + expect(mobile.with['cache-dependency-path'].trim().split('\n')).toEqual([ + 'pnpm-lock.yaml', + 'mobile/pnpm-lock.yaml' + ]) + }) +}) diff --git a/config/scripts/ci-e2e-shard-plan.mjs b/config/scripts/ci-e2e-shard-plan.mjs new file mode 100644 index 00000000000..ea6002451a3 --- /dev/null +++ b/config/scripts/ci-e2e-shard-plan.mjs @@ -0,0 +1,97 @@ +import { readFileSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { pathToFileURL } from 'node:url' +import { + balanceFiles, + compareIds, + readTimingBaseline, + writeAssignment +} from './ci-shard-assignment.mjs' + +export function discoverE2eFiles(report) { + if (report.errors?.length) { + throw new Error('Playwright discovery reported errors') + } + const files = new Map() + function visit(suite) { + for (const spec of suite.specs ?? []) { + const file = spec.file.replaceAll('\\', '/') + if (file.startsWith('/') || file.split('/').includes('..') || /[\n\r>›]/.test(file)) { + throw new Error(`Unsafe test-list path: ${file}`) + } + for (const test of spec.tests) { + const id = `${test.projectName}:${spec.id}` + const ids = files.get(file) ?? [] + ids.push(id) + files.set(file, ids) + } + } + for (const child of suite.suites ?? []) { + visit(child) + } + } + for (const suite of report.suites) { + visit(suite) + } + if (!files.size) { + throw new Error('Playwright discovered no tests') + } + const ids = [...files.values()].flat() + if (new Set(ids).size !== ids.length) { + throw new Error('Duplicate discovered test identity') + } + return Object.fromEntries([...files.entries()].sort(([a], [b]) => compareIds(a, b))) +} + +export function planE2e(report, count, baseline) { + const testsByFile = discoverE2eFiles(report) + const timings = Object.fromEntries( + Object.entries(baseline.timings).map(([file, duration]) => [ + file.replace(/^tests\/e2e\//, ''), + duration + ]) + ) + const assignment = balanceFiles(Object.keys(testsByFile), count, timings) + return { ...assignment, testsByFile, baselineSha256: baseline.baselineSha256 } +} + +export function verifyE2eSelection(assignment, report) { + const actual = Object.values(discoverE2eFiles(report)).flat().sort(compareIds) + const expected = assignment.shards[assignment.selectedShard - 1].files + .flatMap((file) => assignment.testsByFile[file]) + .sort(compareIds) + if (JSON.stringify(actual) !== JSON.stringify(expected)) { + throw new Error('Native Playwright selection differs from shard assignment') + } +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + if (process.argv[2] === '--verify') { + verifyE2eSelection( + JSON.parse(readFileSync(process.argv[3], 'utf8')), + JSON.parse(readFileSync(process.argv[4], 'utf8')) + ) + } else { + const [input, shard, directory] = process.argv.slice(2) + const match = shard?.match(/^(\d+)\/(\d+)$/) + if (!input || !directory || !match) { + throw new Error('Usage: ci-e2e-shard-plan.mjs DISCOVERY INDEX/COUNT OUTPUT_DIRECTORY') + } + const index = Number(match[1]) + const count = Number(match[2]) + if (index < 1 || index > count) { + throw new Error('Invalid shard index') + } + const assignment = planE2e( + JSON.parse(readFileSync(input, 'utf8')), + count, + readTimingBaseline('e2e') + ) + const selected = assignment.shards[index - 1].files + if (!selected.length) { + throw new Error('Empty E2E shard') + } + writeAssignment(join(directory, 'assignment.json'), { ...assignment, selectedShard: index }) + writeFileSync(join(directory, 'selected.txt'), `${selected.join('\n')}\n`) + } +} diff --git a/config/scripts/ci-e2e-shard-selection.test.mjs b/config/scripts/ci-e2e-shard-selection.test.mjs new file mode 100644 index 00000000000..15a031179e7 --- /dev/null +++ b/config/scripts/ci-e2e-shard-selection.test.mjs @@ -0,0 +1,73 @@ +import { mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { dirname, join } from 'node:path' +import { expect, it } from 'vitest' +import { runProcess } from '../../src/shared/child-process/run-process' +import { planE2e, verifyE2eSelection } from './ci-e2e-shard-plan.mjs' + +const require = createRequire(import.meta.url) + +it('native Playwright test-list preserves full discovery, serial suites, skips and headful filtering', async () => { + const directory = realpathSync(mkdtempSync(join(tmpdir(), 'orca-playwright-shards-'))) + const testPackage = JSON.stringify(require.resolve('@stablyai/playwright-test')) + const config = join(directory, 'playwright.config.cjs') + writeFileSync( + config, + `module.exports = { testDir: '.', fullyParallel: true, projects: [{ name: 'electron-headless', grepInvert: /@headful/ }] }` + ) + for (let index = 0; index < 17; index++) { + writeFileSync( + join(directory, `file-${index}.spec.cjs`), + ` + const { test } = require(${testPackage}); + test('normal', () => {}); + test.skip('skipped', () => {}); + test('visible @headful', () => {}); + test.describe.serial('serial', () => { + test('first', () => {}); + test('second', () => {}); + }); + ` + ) + } + async function discover(extra = []) { + const result = await runProcess({ + program: process.execPath, + cwd: directory, + args: [ + join(dirname(require.resolve('playwright/package.json')), 'cli.js'), + 'test', + '--config', + config, + '--list', + '--reporter=json', + ...extra + ], + env: { ...process.env, ORCA_BACKGROUND_LAUNCH: '1' }, + timeoutMs: 20000 + }) + expect(result.code, result.stderr).toBe(0) + return JSON.parse(result.stdout) + } + try { + const full = await discover() + const assignment = planE2e(full, 14, { timings: {} }) + const ids = [] + for (let index = 0; index < 14; index++) { + const path = join(directory, 'selected.txt') + writeFileSync(path, `${assignment.shards[index].files.join('\n')}\n`) + const selected = await discover(['--test-list', path]) + verifyE2eSelection({ ...assignment, selectedShard: index + 1 }, selected) + for (const suite of selected.suites) { + expect(suite.specs.some((spec) => spec.title.includes('@headful'))).toBe(false) + } + ids.push(...assignment.shards[index].files.flatMap((file) => assignment.testsByFile[file])) + } + expect(ids).toHaveLength(17 * 4) + expect(new Set(ids).size).toBe(ids.length) + expect(() => verifyE2eSelection({ ...assignment, selectedShard: 1 }, full)).toThrow('differs') + } finally { + rmSync(directory, { recursive: true, force: true }) + } +}, 60000) diff --git a/config/scripts/ci-shard-assignment.mjs b/config/scripts/ci-shard-assignment.mjs new file mode 100644 index 00000000000..1cb44969a63 --- /dev/null +++ b/config/scripts/ci-shard-assignment.mjs @@ -0,0 +1,66 @@ +import { createHash } from 'node:crypto' +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname } from 'node:path' + +export const compareIds = (a, b) => (a < b ? -1 : a > b ? 1 : 0) + +export function balanceFiles(files, count, timings, overheadMs = 0) { + if (!Number.isInteger(count) || count < 1) { + throw new Error('Invalid shard count') + } + if (new Set(files).size !== files.length) { + throw new Error('Duplicate discovered file') + } + const known = Object.values(timings).filter((value) => Number.isFinite(value) && value > 0) + known.sort((a, b) => a - b) + const fallbackMs = known[Math.floor(known.length / 2)] ?? 1000 + const weighted = files.map((file) => ({ + file, + durationMs: + (Number.isFinite(timings[file]) && timings[file] > 0 ? timings[file] : fallbackMs) + + overheadMs + })) + weighted.sort((a, b) => b.durationMs - a.durationMs || compareIds(a.file, b.file)) + const shards = Array.from({ length: count }, () => ({ files: [], durationMs: 0 })) + for (const entry of weighted) { + const target = shards.reduce((best, shard) => + shard.durationMs < best.durationMs || + (shard.durationMs === best.durationMs && shard.files.length < best.files.length) + ? shard + : best + ) + target.files.push(entry.file) + target.durationMs += entry.durationMs + } + for (const shard of shards) { + shard.files.sort(compareIds) + } + const assigned = shards.flatMap((shard) => shard.files).sort(compareIds) + if (JSON.stringify(assigned) !== JSON.stringify([...files].sort(compareIds))) { + throw new Error('Shard coverage differs from discovery') + } + return { algorithm: 'file-lpt-v1', fallbackMs, overheadMs, shards } +} + +export function readTimingBaseline(suite) { + const bytes = readFileSync(new URL('./ci-shard-timings.json', import.meta.url), 'utf8') + const baseline = JSON.parse(bytes) + return { ...baseline[suite], baselineSha256: createHash('sha256').update(bytes).digest('hex') } +} + +export function writeAssignment(path, assignment) { + mkdirSync(dirname(path), { recursive: true }) + writeFileSync( + path, + `${JSON.stringify( + { + sourceSha: process.env.ORCA_SHARD_SOURCE_SHA ?? process.env.GITHUB_SHA ?? null, + runId: process.env.GITHUB_RUN_ID ?? null, + runAttempt: process.env.GITHUB_RUN_ATTEMPT ?? null, + ...assignment + }, + null, + 2 + )}\n` + ) +} diff --git a/config/scripts/ci-shard-assignment.test.mjs b/config/scripts/ci-shard-assignment.test.mjs new file mode 100644 index 00000000000..607a73d392c --- /dev/null +++ b/config/scripts/ci-shard-assignment.test.mjs @@ -0,0 +1,124 @@ +import { mkdtempSync, readFileSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join, resolve } from 'node:path' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { BaseSequencer } from 'vitest/node' +import { balanceFiles } from './ci-shard-assignment.mjs' +import { discoverE2eFiles, planE2e } from './ci-e2e-shard-plan.mjs' +import { parseTimingLog } from './ci-shard-timing-import.mjs' +import TimingSequencer from './ci-unit-sequencer.mjs' + +const directories = [] +afterEach(() => { + vi.unstubAllEnvs() + for (const directory of directories.splice(0)) { + rmSync(directory, { recursive: true, force: true }) + } +}) + +describe('timing-weighted shard selection', () => { + it('distributes long files, includes unknowns exactly once, and ignores discovery order', () => { + const files = ['long', 'medium', 'short', 'unknown', 'new', 'zero', 'invalid'] + const timings = { long: 100, medium: 80, short: 20, zero: 0, invalid: -1, deleted: 20 } + const plan = balanceFiles(files, 3, timings, 10) + expect(plan).toEqual(balanceFiles(files.toReversed(), 3, timings, 10)) + expect(plan.fallbackMs).toBe(80) + expect(plan.shards.flatMap((shard) => shard.files).sort()).toEqual([...files].sort()) + expect(Math.max(...plan.shards.map((shard) => shard.durationMs))).toBeLessThan(250) + }) + + it('has a deterministic cold fallback and permits fewer files than shards', () => { + expect(balanceFiles(['b', 'a'], 3, {}).shards).toEqual([ + { files: ['a'], durationMs: 1000 }, + { files: ['b'], durationMs: 1000 }, + { files: [], durationMs: 0 } + ]) + expect(() => balanceFiles(['a', 'a'], 8, {})).toThrow('Duplicate') + expect(() => balanceFiles(['a'], 0, {})).toThrow('count') + }) + + it('uses the post-filter Vitest discovery unchanged across eight shards and retains default sort', async () => { + const directory = mkdtempSync(join(tmpdir(), 'orca-unit-shards-')) + directories.push(directory) + vi.stubEnv('ORCA_SHARD_MANIFEST', join(directory, 'assignment.json')) + const specs = Array.from({ length: 37 }, (_, i) => ({ + moduleId: resolve(`src/fixture-${i}.test.ts`) + })) + const selected = [] + for (let index = 1; index <= 8; index++) { + const sequencer = new TimingSequencer({ + config: { root: process.cwd(), shard: { index, count: 8 } } + }) + expect(sequencer.sort).toBe(BaseSequencer.prototype.sort) + selected.push(...(await sequencer.shard(specs))) + const manifest = JSON.parse(readFileSync(join(directory, 'assignment.json'), 'utf8')) + expect(manifest.selectedShard).toBe(index) + expect(manifest.baselineSha256).toMatch(/^[a-f0-9]{64}$/) + } + expect(new Set(selected).size).toBe(specs.length) + expect(selected).toHaveLength(specs.length) + expect(new Set(selected)).toEqual(new Set(specs)) + }) + + it('wires a constructor into the opt-in Vitest config', async () => { + vi.stubEnv('ORCA_BALANCE_UNIT_SHARDS', '1') + const { default: config } = await import('../vitest.config') + expect(config.test.sequence.sequencer).toBe(TimingSequencer) + }) + + it('keeps nested/serial E2E files atomic and fails closed on discovery errors', () => { + const spec = (id, file) => ({ id, file, tests: [{ projectName: 'electron-headless' }] }) + const report = { + suites: [ + { + specs: [spec('a', 'one.spec.ts')], + suites: [{ specs: [spec('b', 'one.spec.ts'), spec('c', 'two.spec.ts')] }] + } + ] + } + const plan = planE2e(report, 14, { timings: { 'tests/e2e/one.spec.ts': 4000 } }) + expect(plan.shards.flatMap((shard) => shard.files).sort()).toEqual([ + 'one.spec.ts', + 'two.spec.ts' + ]) + expect(plan.testsByFile['one.spec.ts']).toHaveLength(2) + expect(() => discoverE2eFiles({ ...report, errors: [{}] })).toThrow('errors') + expect(() => discoverE2eFiles({ suites: [] })).toThrow('no tests') + expect(() => + discoverE2eFiles({ suites: [{ specs: [spec('a', '../escape.spec.ts')] }] }) + ).toThrow('Unsafe') + expect(() => + discoverE2eFiles({ + suites: [{ specs: [spec('a', 'one.spec.ts'), spec('a', 'one.spec.ts')] }] + }) + ).toThrow('Duplicate') + }) + + it('imports ANSI unit timings and E2E failures without counting headful reruns', () => { + const parsed = parseTimingLog( + [ + '\u001b[32m✓\u001b[39m src/a.test.ts (2 tests) 35ms', + 'Duration 1s (transform 0.1s, setup 0.2s, import 0.3s, tests 0.04s, environment 0.4s)', + '✓ 1 [electron-headless] › tests/e2e/a.spec.ts:1:1 › works (2s)', + '✘ 2 [electron-headless] › tests/e2e/a.spec.ts:2:1 › fails (1.2m)', + '✓ 3 [electron-headful] › tests/e2e/a.spec.ts:3:1 › benchmark (9s)' + ].join('\n') + ) + expect(parsed).toEqual({ + unit: { 'src/a.test.ts': 35 }, + e2e: { 'tests/e2e/a.spec.ts': 74000 }, + overheadMs: 1000 + }) + }) + + it('reads mixed units from captured Vitest output', () => { + const parsed = parseTimingLog( + 'Duration 5.14s (transform 952ms, setup 449ms, import 1.18s, tests 9.41s, environment 1ms)' + ) + expect(parsed.overheadMs).toBe(2582) + }) + + it('rejects incomplete unit evidence instead of silently dropping overhead', () => { + expect(() => parseTimingLog('✓ src/a.test.ts (2 tests) 35ms')).toThrow('Duration summary') + }) +}) diff --git a/config/scripts/ci-shard-timing-import.mjs b/config/scripts/ci-shard-timing-import.mjs new file mode 100644 index 00000000000..5b62c0f9f7f --- /dev/null +++ b/config/scripts/ci-shard-timing-import.mjs @@ -0,0 +1,86 @@ +import { readdirSync, readFileSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { stripVTControlCharacters } from 'node:util' +import { pathToFileURL } from 'node:url' + +export function parseTimingLog(text) { + const clean = stripVTControlCharacters(text) + const unit = {} + const e2e = {} + for (const match of clean.matchAll( + /[✓×❯] ([\w./-]+\.test\.(?:ts|tsx|mjs)) \([^\n]*?\)\s+([\d.]+)ms/g + )) { + unit[match[1]] = Number(match[2]) + } + for (const match of clean.matchAll( + /[✓✘]\s+\d+ \[electron-headless\] › (tests\/e2e\/[^:]+):\d+:\d+ › .*? \(([\d.]+)(ms|s|m)\)/g + )) { + e2e[match[1]] = (e2e[match[1]] ?? 0) + Number(match[2]) * { ms: 1, s: 1000, m: 60000 }[match[3]] + } + const summary = clean.match( + /Duration\s+[\d.]+(?:ms|s) \(transform ([\d.]+(?:ms|s)), setup ([\d.]+(?:ms|s)), import ([\d.]+(?:ms|s)), tests [\d.]+(?:ms|s), environment ([\d.]+(?:ms|s))\)/ + ) + if (Object.keys(unit).length && !summary) { + throw new Error('Unit timing log has no supported Duration summary') + } + return { + unit, + e2e, + overheadMs: summary + ? summary + .slice(1) + .reduce( + (sum, value) => sum + Number.parseFloat(value) * (value.endsWith('ms') ? 1 : 1000), + 0 + ) + : 0 + } +} + +export function importTimingLogs(directory, unitRun, e2eRun) { + const baseline = { + unit: { runId: unitRun, jobIds: [], overheadMs: 0, timings: {} }, + e2e: { runId: e2eRun, jobIds: [], overheadMs: 0, timings: {} } + } + for (const file of readdirSync(directory) + .filter((file) => /^log-\d+\.txt$/.test(file)) + .sort()) { + const parsed = parseTimingLog(readFileSync(join(directory, file), 'utf8')) + for (const suite of ['unit', 'e2e']) { + if (!Object.keys(parsed[suite]).length) { + continue + } + baseline[suite].jobIds.push(file.match(/\d+/)[0]) + for (const [name, duration] of Object.entries(parsed[suite])) { + if (suite === 'unit' && name in baseline.unit.timings) { + throw new Error(`Duplicate unit timing: ${name}`) + } + baseline[suite].timings[name] = (baseline[suite].timings[name] ?? 0) + duration + } + } + baseline.unit.overheadMs += parsed.overheadMs + } + for (const suite of ['unit', 'e2e']) { + if (!baseline[suite].jobIds.length) { + throw new Error(`No ${suite} timing evidence`) + } + baseline[suite].timings = Object.fromEntries( + Object.entries(baseline[suite].timings).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) + ) + } + baseline.unit.overheadMs = Math.ceil( + baseline.unit.overheadMs / Object.keys(baseline.unit.timings).length + ) + return baseline +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const [directory, unitRun, e2eRun, output] = process.argv.slice(2) + if (!directory || !unitRun || !e2eRun || !output) { + throw new Error('Usage: ci-shard-timing-import.mjs LOG_DIRECTORY UNIT_RUN E2E_RUN OUTPUT') + } + writeFileSync( + output, + `${JSON.stringify(importTimingLogs(directory, unitRun, e2eRun), null, 2)}\n` + ) +} diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json new file mode 100644 index 00000000000..46ea33ce679 --- /dev/null +++ b/config/scripts/ci-shard-timings.json @@ -0,0 +1,8815 @@ +{ + "unit": { + "runId": "34675583768", + "jobIds": [ + "103505449197", + "103505449205", + "103505449211", + "103505449212", + "103505449228", + "103505449235", + "103505449248", + "103505449256" + ], + "overheadMs": 526, + "timings": { + "config/scripts/adhoc-build-version.test.mjs": 31, + "config/scripts/agent-status-hot-path-benchmark.test.ts": 2277, + "config/scripts/app-store-performance-plugin.test.mjs": 912, + "config/scripts/audit-localization-coverage.test.mjs": 23, + "config/scripts/benchmark-artifact-comparison.test.mjs": 132, + "config/scripts/benchmark-sample-summary.test.mjs": 8, + "config/scripts/build-linux-local.test.mjs": 15, + "config/scripts/build-mac-local.test.mjs": 5, + "config/scripts/build-orcad-prebuilds.test.mjs": 27, + "config/scripts/build-windows-cli-launcher.test.mjs": 39, + "config/scripts/check-changed-code-quality.test.mjs": 7, + "config/scripts/check-max-lines-ratchet.test.mjs": 10, + "config/scripts/check-react-doctor-changed.test.mjs": 189, + "config/scripts/check-reliability-gates.test.mjs": 171, + "config/scripts/check-root-directory-entries.test.mjs": 880, + "config/scripts/check-runtime-electron-ratchet.test.mjs": 1931, + "config/scripts/check-terminal-perf-report-budgets.test.mjs": 398, + "config/scripts/check-ts-nocheck-ratchet.test.mjs": 5, + "config/scripts/ci-native-toolchain.test.mjs": 38, + "config/scripts/client-hosted-browser-package-coverage.test.mjs": 81, + "config/scripts/codex-index-heal-contract-workflow.test.mjs": 10, + "config/scripts/codex-primary-home-tripwire.test.ts": 421, + "config/scripts/computer-e2e-workflow.test.mjs": 132, + "config/scripts/computer-use-modifier-safety.test.mjs": 5, + "config/scripts/computer-use-mouse-button-routing.test.mjs": 9, + "config/scripts/computer-use-skill-guidance.test.mjs": 15, + "config/scripts/computer-use-smoke.test.mjs": 471, + "config/scripts/computer-use-windows-horizontal-scroll.test.mjs": 3, + "config/scripts/counterbalanced-benchmark-schedule.test.mjs": 10, + "config/scripts/create-draft-release.test.mjs": 18, + "config/scripts/daily-build-version.test.mjs": 36, + "config/scripts/daily-e2e-dispatch-contract.test.mjs": 6, + "config/scripts/dev-channel-base-version.test.mjs": 8, + "config/scripts/dev-channel-windows-workflow-contract.test.mjs": 218, + "config/scripts/dev-cli-terminal-wrapper.test.mjs": 9, + "config/scripts/dev-electron-bundle-cache.test.ts": 10, + "config/scripts/dev-electron-bundle-identity.test.ts": 7, + "config/scripts/electron-builder-config.test.mjs": 155, + "config/scripts/electron-builder-mac-channel-config.test.mjs": 577, + "config/scripts/electron-builder-markdown-associations.test.mjs": 14, + "config/scripts/electron-builder-native-rebuild.test.mjs": 11, + "config/scripts/electron-builder-runtime-resources.test.mjs": 393, + "config/scripts/electron-builder-speech-config.test.mjs": 16, + "config/scripts/electron-runtime-floor.test.ts": 5, + "config/scripts/electron-vite-output-contract.test.ts": 13, + "config/scripts/ensure-native-runtime-job-ownership.test.mjs": 10, + "config/scripts/ensure-native-runtime.test.mjs": 422, + "config/scripts/generate-bundled-skill-guides.test.mjs": 293, + "config/scripts/generate-runtime-required-english-catalog.test.mjs": 8, + "config/scripts/generate-skill-bundle-manifest.test.mjs": 448, + "config/scripts/generate-terminal-perf-html-report.test.mjs": 19, + "config/scripts/git-binary-compatibility-workflow.test.mjs": 98, + "config/scripts/git-pull-request-diff-base.test.mjs": 4, + "config/scripts/hang-watchdog-process-metrics.test.mjs": 6, + "config/scripts/happy-dom-mutation-observer-retention.test.ts": 100, + "config/scripts/happy-dom-offscreen-canvas.test.ts": 42, + "config/scripts/headless-serve-shutdown-workflow.test.mjs": 15, + "config/scripts/hourly-build-version.test.mjs": 27, + "config/scripts/hourly-preflight-workflow.test.mjs": 33, + "config/scripts/idle-cpu-process-sampling.test.mjs": 5, + "config/scripts/install-electron-package-binary.test.mjs": 2145, + "config/scripts/install-node-dependencies-action.test.mjs": 140, + "config/scripts/latest-stable-release.test.mjs": 10, + "config/scripts/lint-staged-worktree-backup.test.mjs": 276, + "config/scripts/linux-package-maintainer-scripts.test.mjs": 6, + "config/scripts/live-freeze-bounded-history.test.mjs": 6, + "config/scripts/live-remote-bulk-open-freeze-metrics.test.mjs": 17, + "config/scripts/live-remote-freeze-rpc.test.mjs": 5, + "config/scripts/live-remote-status-watchdog.test.mjs": 248, + "config/scripts/locale-count-fragment-separator.test.mjs": 17, + "config/scripts/locale-generic-ui-terms.test.mjs": 20, + "config/scripts/locale-ko-frozen-terminal-search-keywords.test.mjs": 35, + "config/scripts/locale-ko-key-overrides.test.mjs": 38, + "config/scripts/locale-repair-catalog-missing-leaves.test.mjs": 14, + "config/scripts/locale-translation-policy-ko-round5.test.mjs": 12, + "config/scripts/locale-translation-policy.es-pr.test.mjs": 13, + "config/scripts/locale-translation-policy.es-round5.test.mjs": 9, + "config/scripts/locale-translation-policy.ja-relocalization.test.mjs": 17, + "config/scripts/locale-translation-policy.ja-round5.test.mjs": 13, + "config/scripts/locale-translation-policy.test.mjs": 38, + "config/scripts/locale-translation-policy.zh-round5.test.mjs": 14, + "config/scripts/locale-translation-policy.zh-status-bar-usage.test.mjs": 24, + "config/scripts/localization-package-contract.test.mjs": 8, + "config/scripts/mac-build-compatibility.test.mjs": 8, + "config/scripts/macos-computer-helper-owner-loss-group-recovery.test.mjs": 10, + "config/scripts/macos-tcc-prompt-localization.test.mjs": 75, + "config/scripts/mobile-pairing-qrcode-import-plugin.test.mjs": 450, + "config/scripts/node-old-space-limit.test.mjs": 6, + "config/scripts/node-pty-console-list-agent-patch.test.mjs": 7, + "config/scripts/node-pty-master-cloexec-patch.test.mjs": 22, + "config/scripts/node-pty-windows-pty-teardown-patch.test.mjs": 59, + "config/scripts/orca-cli-skill-guidance.test.mjs": 14, + "config/scripts/orca-dev-bin.test.mjs": 68, + "config/scripts/orca-linear-skill-guidance.test.mjs": 15, + "config/scripts/orcad-operations-restart-safety.test.mjs": 5, + "config/scripts/orchestration-guide-command-contract.test.mjs": 11, + "config/scripts/orchestration-skill-guidance.test.mjs": 23, + "config/scripts/oxc-cli-invocation.test.mjs": 128, + "config/scripts/oxlint-cli-invocation.test.mjs": 46, + "config/scripts/package-electron-runtime-contract.test.mjs": 360, + "config/scripts/packaged-browser-lane-contract.test.mjs": 7, + "config/scripts/packaged-hang-watchdog-worker-contract.test.mjs": 55, + "config/scripts/packaged-node-pty-prebuild-prune.test.mjs": 25, + "config/scripts/packaged-source-map-prune.test.mjs": 53, + "config/scripts/patched-dependencies-frozen-install.test.mjs": 451, + "config/scripts/plain-node-entry-guard.test.ts": 610, + "config/scripts/pnpm-cli-invocation.test.mjs": 6, + "config/scripts/pr-code-change-scope.test.mjs": 346, + "config/scripts/pr-e2e-gate-contract.test.mjs": 108, + "config/scripts/pr-e2e-native-only-routing.test.mjs": 19, + "config/scripts/pr-test-loc-summary.test.mjs": 89, + "config/scripts/pr-workflow-lint-parity.test.mjs": 53, + "config/scripts/pr-workflow-parallelism.test.mjs": 898, + "config/scripts/project-renderer-web-client.test.mjs": 213, + "config/scripts/pty-transcript-secret-scan.test.mjs": 14, + "config/scripts/publish-complete-draft-releases.test.mjs": 168, + "config/scripts/quadratic-buffer-concat-plugin.test.mjs": 2010, + "config/scripts/rebuild-native-deps-node-pty.test.mjs": 2338, + "config/scripts/rebuild-native-deps.test.mjs": 831, + "config/scripts/reclaim-dev-electron-bundles.test.ts": 35, + "config/scripts/regenerate-xterm-patches.test.mjs": 300, + "config/scripts/relay-artifact-manifest.test.mjs": 3554, + "config/scripts/relay-asset-line-ending-pin.test.mjs": 74, + "config/scripts/release-blocker-fixes.test.mjs": 39, + "config/scripts/release-cut-signpath-slack.test.mjs": 72, + "config/scripts/release-cut-sourcemap-publish.test.mjs": 11, + "config/scripts/release-cut-token-permissions.test.mjs": 19, + "config/scripts/release-e2e-dispatch-contract.test.mjs": 8, + "config/scripts/release-mac-build-workflow-dispatch.test.mjs": 10, + "config/scripts/release-rc-history.test.mjs": 166, + "config/scripts/renderer-boot-graph.test.mjs": 54, + "config/scripts/renderer-scrollbar-style-plugin.test.mjs": 2888, + "config/scripts/replace-cached-nsis-elevate.test.mjs": 1880, + "config/scripts/resolve-7za-path.test.mjs": 1412, + "config/scripts/run-codex-real-account-validation.test.ts": 3652, + "config/scripts/run-internal-dev-setup.test.mjs": 10, + "config/scripts/run-linux-packaged-node-pty-floor-smoke.test.mjs": 10, + "config/scripts/run-terminal-scale-perf-report-gate.test.mjs": 17, + "config/scripts/shared-electron-dist-cache.test.ts": 44, + "config/scripts/shebang-script-line-ending-pin.test.mjs": 77, + "config/scripts/skill-critical-guidance.test.mjs": 6, + "config/scripts/skill-description-length.test.mjs": 26, + "config/scripts/skill-recipe-shell.test.mjs": 43, + "config/scripts/skill-sharing-release-workflow.test.mjs": 12, + "config/scripts/skill-update-roundtrip-workflow.test.mjs": 5, + "config/scripts/skills-cli-package-workflow.test.mjs": 8, + "config/scripts/sort-comparator-performance-plugin.test.mjs": 269, + "config/scripts/space-sharing-copy.test.ts": 40, + "config/scripts/ssh-browser-e2e-routing.test.mjs": 23, + "config/scripts/ssh-localhost-e2e-routing.test.mjs": 28, + "config/scripts/static-appimage-package-contract.test.mjs": 42, + "config/scripts/summarize-terminal-perf-report.test.mjs": 52, + "config/scripts/telemetry-bundle-constant-patterns.test.mjs": 4, + "config/scripts/terminal-ime-e2e-workflow.test.mjs": 9, + "config/scripts/terminal-ime-engagement-receipt.test.mjs": 11, + "config/scripts/terminal-perf-report-annotations.test.mjs": 4, + "config/scripts/trim-windows-icon-source.test.mjs": 121, + "config/scripts/verify-cli-bin.test.mjs": 64, + "config/scripts/verify-dev-channel-packaging.test.mjs": 666, + "config/scripts/verify-linux-glibc-floor.test.mjs": 118, + "config/scripts/verify-localization-catalog.test.mjs": 46, + "config/scripts/verify-localization-extraction.test.mjs": 5, + "config/scripts/verify-packaged-browser-participation.test.mjs": 10, + "config/scripts/verify-packaged-daemon-entry.test.mjs": 111, + "config/scripts/verify-packaged-node-pty-job-ownership.test.mjs": 10, + "config/scripts/verify-packaged-plugin-resources.test.mjs": 67, + "config/scripts/verify-release-required-assets.test.mjs": 8, + "config/scripts/verify-skills-cli-runtime.test.mjs": 141, + "config/scripts/verify-windows-inner-signature.test.mjs": 20, + "config/scripts/verify-wsl-e2e-participation.test.mjs": 9, + "config/scripts/websocket-server-loopback-bind.test.ts": 5, + "config/scripts/win-crash-survival-e2e.test.mjs": 12, + "config/scripts/win32-test-lane-registration.test.mjs": 22, + "config/scripts/windows-cmd-shim-spawn-boundary.test.mjs": 6, + "config/scripts/windows-process-tree-gyp-path.test.mjs": 42, + "config/scripts/windows-process-tree-gyp-rebuild.test.mjs": 49, + "config/scripts/windows-process-tree-patch-contract.test.mjs": 530, + "config/scripts/windows-pty-native-capability-workflow.test.mjs": 7, + "config/scripts/windows-signing-gate-toolset.test.mjs": 53, + "config/scripts/windows-signing-workflow-contract.test.mjs": 531, + "config/scripts/windows-uninstaller-signing.test.mjs": 17, + "config/scripts/workflow-ref-mirror-case-safety.test.mjs": 74, + "config/scripts/workflow-ref-reachability.test.mjs": 592, + "config/scripts/wsl-e2e-lane-contract.test.mjs": 76, + "config/scripts/xterm-webgl-runtime-contract.test.mjs": 19, + "src/cli/agent-context.test.ts": 16, + "src/cli/args.test.ts": 36, + "src/cli/automation-format.test.ts": 16, + "src/cli/automation-owner-conflict-recovery.test.ts": 12, + "src/cli/base64-payload-byte-count.test.ts": 3, + "src/cli/browser-cookie-credentials-empty-value.test.ts": 58, + "src/cli/browser-storage-empty-value.test.ts": 48, + "src/cli/browser.test.ts": 109, + "src/cli/cli-command-name-parity.test.ts": 7, + "src/cli/cli-version.test.ts": 13, + "src/cli/codex-command-classification.test.ts": 11, + "src/cli/command-suggestion-budget.test.ts": 25, + "src/cli/command-suggestion.test.ts": 20, + "src/cli/computer-format.test.ts": 3, + "src/cli/emulator-logcat-format.test.ts": 4, + "src/cli/execution-host-flag.test.ts": 21, + "src/cli/flags.test.ts": 4, + "src/cli/format-recovery.test.ts": 10, + "src/cli/format.test.ts": 29, + "src/cli/handler-group-manifest.test.ts": 638, + "src/cli/handlers/account.test.ts": 558, + "src/cli/handlers/agent-hooks.test.ts": 83, + "src/cli/handlers/artifacts.test.ts": 36, + "src/cli/handlers/automation-destination-fencing.test.ts": 17, + "src/cli/handlers/automation-owner-fencing.test.ts": 16, + "src/cli/handlers/computer-action-routing.test.ts": 69, + "src/cli/handlers/computer-action-validation.test.ts": 86, + "src/cli/handlers/computer-state-formatting.test.ts": 65, + "src/cli/handlers/computer.test.ts": 103, + "src/cli/handlers/core.test.ts": 7, + "src/cli/handlers/emulator.test.ts": 37, + "src/cli/handlers/file-absolute-paths.test.ts": 60, + "src/cli/handlers/file.test.ts": 46, + "src/cli/handlers/interactive-login-interruption.test.ts": 12, + "src/cli/handlers/linear.test.ts": 131, + "src/cli/handlers/orchestration-caller-identity-cli.test.ts": 18, + "src/cli/handlers/orchestration-check-identity.test.ts": 12, + "src/cli/handlers/orchestration-federated-legacy-settlement.test.ts": 8, + "src/cli/handlers/orchestration-gate-cli.test.ts": 99, + "src/cli/handlers/orchestration-legacy-read-only.test.ts": 9, + "src/cli/handlers/orchestration-lifecycle-json-rejection.test.ts": 15, + "src/cli/handlers/orchestration-lifecycle-rejection.test.ts": 12, + "src/cli/handlers/orchestration-migration.test.ts": 12, + "src/cli/handlers/orchestration-module-boundaries.test.ts": 11, + "src/cli/handlers/orchestration-request-show-cli.test.ts": 7, + "src/cli/handlers/orchestration-run-cli.test.ts": 22, + "src/cli/handlers/orchestration-send-receipt-warnings.test.ts": 7, + "src/cli/handlers/orchestration-task-create-cli.test.ts": 8, + "src/cli/handlers/orchestration-task-list-brief.test.ts": 12, + "src/cli/handlers/orchestration-timeout-cli.test.ts": 29, + "src/cli/handlers/orchestration-timeout.test.ts": 26, + "src/cli/handlers/orchestration-windows-ask-cli.test.ts": 11, + "src/cli/handlers/orchestration-worker-cli.test.ts": 32, + "src/cli/handlers/orchestration-worker-show-wait-cli.test.ts": 9, + "src/cli/handlers/orchestration.test.ts": 31, + "src/cli/handlers/orchestration/worker-list-run-scope.test.ts": 13, + "src/cli/handlers/orchestration/worker-output.test.ts": 13, + "src/cli/handlers/skill-sharing.test.ts": 16, + "src/cli/handlers/terminal.test.ts": 29, + "src/cli/host-selector-alternatives.test.ts": 14, + "src/cli/index-automation-identifiers.test.ts": 42, + "src/cli/index-automation-schedule.test.ts": 64, + "src/cli/index-automation-session-reuse.test.ts": 54, + "src/cli/index-automation-source-context.test.ts": 75, + "src/cli/index-automation-target.test.ts": 89, + "src/cli/index-device-commands.test.ts": 57, + "src/cli/index-environment-commands.test.ts": 103, + "src/cli/index-local-command-routing-flags.test.ts": 141, + "src/cli/index-memory-diagnostics.test.ts": 54, + "src/cli/index-omitted-host-scope-selectors.test.ts": 54, + "src/cli/index-orchestration.test.ts": 72, + "src/cli/index-project-setup.test.ts": 95, + "src/cli/index-serve-command.test.ts": 121, + "src/cli/index-terminal-commands.test.ts": 78, + "src/cli/index-terminal-list-host-scope.test.ts": 92, + "src/cli/index-vm-recipe-doctor.test.ts": 125, + "src/cli/index-worktree-create-agent.test.ts": 64, + "src/cli/index-worktree-create-linear.test.ts": 44, + "src/cli/index-worktree-create-parent.test.ts": 118, + "src/cli/index-worktree-create-target.test.ts": 65, + "src/cli/index-worktree-selector-resolution.test.ts": 185, + "src/cli/index-worktree-set.test.ts": 83, + "src/cli/index.test.ts": 263, + "src/cli/linear-format.test.ts": 9, + "src/cli/main-module-bundle-parity.test.ts": 14, + "src/cli/orchestration-dispatch-refusal-format.test.ts": 8, + "src/cli/orchestration-mutation-recovery.test.ts": 52, + "src/cli/orchestration-structured-sender-identity.test.ts": 93, + "src/cli/orchestration-structured-session-no-identity.test.ts": 11, + "src/cli/quote-stripped-json-flag.test.ts": 8, + "src/cli/registry-parity.test.ts": 7, + "src/cli/retry-request-flag.test.ts": 9, + "src/cli/runtime-client-deferral.test.ts": 486, + "src/cli/runtime-client.test.ts": 110, + "src/cli/runtime/client-recovery.test.ts": 250, + "src/cli/runtime/client-timeout-policy.test.ts": 13, + "src/cli/runtime/envelope-schema.test.ts": 33, + "src/cli/runtime/environments.test.ts": 27, + "src/cli/runtime/launch.test.ts": 53, + "src/cli/runtime/orchestration-recovery-command.test.ts": 5, + "src/cli/runtime/serve-signal-exit-diagnostic.test.ts": 28, + "src/cli/runtime/status.test.ts": 19, + "src/cli/runtime/transport-framing.test.ts": 78, + "src/cli/runtime/transport.test.ts": 533, + "src/cli/runtime/types.test.ts": 5, + "src/cli/runtime/websocket-transport-error.test.ts": 8, + "src/cli/runtime/websocket-transport.test.ts": 176, + "src/cli/serve-electron-flag-parity.test.ts": 8, + "src/cli/shell-command-quote.test.ts": 8, + "src/cli/skill-guide-cli-parity.test.ts": 14, + "src/cli/skills-reference-selector.test.ts": 28, + "src/cli/skills.test.ts": 963, + "src/cli/specs/account.test.ts": 6, + "src/cli/specs/bundled-guide-flags.test.ts": 40, + "src/cli/specs/computer.test.ts": 9, + "src/cli/specs/orchestration.test.ts": 16, + "src/cli/specs/skills.test.ts": 6, + "src/cli/terminal-format-draft.test.ts": 6, + "src/cli/terminal-format.test.ts": 12, + "src/cli/terminal-list-host-scope-format.test.ts": 8, + "src/cli/terminal-read-screen.test.ts": 68, + "src/cli/vocabulary-policy.test.ts": 8, + "src/cli/worktree-selector-wsl-posix-path.test.ts": 11, + "src/main/active-view-persistence-boundary.test.ts": 725, + "src/main/active-view-preference-sync-flush-veto.test.ts": 777, + "src/main/active-view-preference.test.ts": 20, + "src/main/agent-auth-restart-preservation.test.ts": 16, + "src/main/agent-awake-service-platform-assertions.test.ts": 10, + "src/main/agent-awake-service.test.ts": 29, + "src/main/agent-hooks/branch-rename-failure-output.test.ts": 7, + "src/main/agent-hooks/ended-process-reconciliation.test.ts": 34, + "src/main/agent-hooks/first-work-branch-rename.test.ts": 140, + "src/main/agent-hooks/first-work-folder-rename.test.ts": 13, + "src/main/agent-hooks/hook-script-outside-orca.test.ts": 32, + "src/main/agent-hooks/hook-status-session-tabs-republish.test.ts": 22, + "src/main/agent-hooks/install-telemetry.test.ts": 10, + "src/main/agent-hooks/installer-utils-remote.test.ts": 14, + "src/main/agent-hooks/installer-utils.test.ts": 66, + "src/main/agent-hooks/local-agent-cli-presence.test.ts": 11, + "src/main/agent-hooks/managed-agent-hook-controls.test.ts": 76, + "src/main/agent-hooks/managed-hook-detection-commands.test.ts": 5, + "src/main/agent-hooks/managed-hook-install-lock.test.ts": 263, + "src/main/agent-hooks/managed-hook-local-filesystem.test.ts": 147, + "src/main/agent-hooks/managed-hook-owner-identity.test.ts": 33, + "src/main/agent-hooks/managed-hook-runtime.test.ts": 41, + "src/main/agent-hooks/managed-hook-script-refresh-main-thread.test.ts": 27, + "src/main/agent-hooks/managed-hook-script-refresh.test.ts": 108, + "src/main/agent-hooks/managed-hook-stdin-lifecycle.test.ts": 1979, + "src/main/agent-hooks/managed-hook-timeout.test.ts": 1585, + "src/main/agent-hooks/managed-toml-ownership.test.ts": 12, + "src/main/agent-hooks/manual-compact-hook-stream.test.ts": 119, + "src/main/agent-hooks/manual-compact-status-cleanup.test.ts": 6, + "src/main/agent-hooks/migration-unsupported-pty-state.test.ts": 8, + "src/main/agent-hooks/opencode-message-part-flood-bench.test.ts": 1234, + "src/main/agent-hooks/remote-hook-service-installers.test.ts": 89, + "src/main/agent-hooks/restored-subagent-liveness-sweep.test.ts": 75, + "src/main/agent-hooks/server-ai-vault-liveness.test.ts": 34, + "src/main/agent-hooks/server-amp-normalization.test.ts": 13, + "src/main/agent-hooks/server-authority-evidence.test.ts": 21, + "src/main/agent-hooks/server-claude-child-permission-lifecycle.test.ts": 213, + "src/main/agent-hooks/server-claude-normalization.test.ts": 36, + "src/main/agent-hooks/server-claude-permission-visibility.test.ts": 114, + "src/main/agent-hooks/server-claude-statusline.test.ts": 41, + "src/main/agent-hooks/server-closed-tab-suppression.test.ts": 167, + "src/main/agent-hooks/server-codex-normalization.test.ts": 14, + "src/main/agent-hooks/server-codex-subagent-transcript.test.ts": 4092, + "src/main/agent-hooks/server-copilot-normalization.test.ts": 416, + "src/main/agent-hooks/server-cursor-normalization.test.ts": 13, + "src/main/agent-hooks/server-droid-normalization.test.ts": 10, + "src/main/agent-hooks/server-endpoint-file-lifecycle.test.ts": 35, + "src/main/agent-hooks/server-gemini-normalization.test.ts": 13, + "src/main/agent-hooks/server-grok-discovery.test.ts": 513, + "src/main/agent-hooks/server-hook-http-ingest.test.ts": 94, + "src/main/agent-hooks/server-ingest-remote.test.ts": 45, + "src/main/agent-hooks/server-ingest-structured-status.test.ts": 34, + "src/main/agent-hooks/server-ingest-terminal-status.test.ts": 18, + "src/main/agent-hooks/server-interrupt-inference-guards.test.ts": 76, + "src/main/agent-hooks/server-interrupt-inference-resurrection.test.ts": 35, + "src/main/agent-hooks/server-interrupt-inference-validation.test.ts": 24, + "src/main/agent-hooks/server-last-status-hydrate-confirmation.test.ts": 75, + "src/main/agent-hooks/server-last-status-hydrate-validation.test.ts": 83, + "src/main/agent-hooks/server-last-status-lead-boundary.test.ts": 172, + "src/main/agent-hooks/server-last-status-restored-children.test.ts": 596, + "src/main/agent-hooks/server-last-status-write.test.ts": 125, + "src/main/agent-hooks/server-observation-provenance.test.ts": 27, + "src/main/agent-hooks/server-opencode-lifecycle.test.ts": 228, + "src/main/agent-hooks/server-opencode-normalization.test.ts": 15, + "src/main/agent-hooks/server-pane-authority.test.ts": 254, + "src/main/agent-hooks/server-pi-normalization.test.ts": 14, + "src/main/agent-hooks/server-prompt-sent-telemetry.test.ts": 59, + "src/main/agent-hooks/server-relay-listener-replay.test.ts": 25, + "src/main/agent-hooks/server-reminted-pane-key.test.ts": 112, + "src/main/agent-hooks/server-replay-evidence-clock.test.ts": 28, + "src/main/agent-hooks/server-retired-pane-new-turn.test.ts": 21, + "src/main/agent-hooks/server-start-failure-lifecycle.test.ts": 32, + "src/main/agent-hooks/server-status-listener-fanout.test.ts": 84, + "src/main/agent-hooks/server-transport-interference.test.ts": 5826, + "src/main/agent-hooks/server.claude-interactive-question.test.ts": 21, + "src/main/agent-hooks/spool.test.ts": 134, + "src/main/agent-hooks/terminal-handle-row-identity.test.ts": 40, + "src/main/agent-hooks/windows-direct-cmd-hook-command.test.ts": 8, + "src/main/agent-hooks/windows-hook-payload-delivery.test.ts": 8, + "src/main/agent-hooks/windows-hook-post-interpreter.test.ts": 83, + "src/main/agent-hooks/windows-powershell-hook-launcher.test.ts": 5, + "src/main/agent-hooks/wsl-guest-plugin-install.test.ts": 9, + "src/main/agent-hooks/wsl-hook-relay-launch.test.ts": 7, + "src/main/agent-hooks/wsl-hook-relay-live.integration.test.ts": 2716, + "src/main/agent-hooks/wsl-hook-relay-manager.test.ts": 1110, + "src/main/agent-hooks/wsl-hook-relay-reattach.test.ts": 6, + "src/main/agent-hooks/wsl-hook-relay-recovery.test.ts": 2323, + "src/main/agent-hooks/wsl-hook-relay-sentinel.test.ts": 17, + "src/main/agent-state-file-reader.test.ts": 39, + "src/main/agent-trust-presets.test.ts": 33, + "src/main/ai-vault-search/session-search-content-hash.test.ts": 5, + "src/main/ai-vault-search/session-search-cwd-key.test.ts": 9, + "src/main/ai-vault-search/session-search-deleted-sources.test.ts": 176, + "src/main/ai-vault-search/session-search-directory-listings.test.ts": 10, + "src/main/ai-vault-search/session-search-engine.test.ts": 1166, + "src/main/ai-vault-search/session-search-file-write.test.ts": 423, + "src/main/ai-vault-search/session-search-fts5-contract.test.ts": 62, + "src/main/ai-vault-search/session-search-hit-ranking.test.ts": 13, + "src/main/ai-vault-search/session-search-identifier-split.test.ts": 8, + "src/main/ai-vault-search/session-search-index-consumer.test.ts": 172, + "src/main/ai-vault-search/session-search-index-generation.test.ts": 111, + "src/main/ai-vault-search/session-search-index-pass.test.ts": 134, + "src/main/ai-vault-search/session-search-index-writer.test.ts": 114, + "src/main/ai-vault-search/session-search-indexer.test.ts": 2283, + "src/main/ai-vault-search/session-search-lifecycle-matrix.test.ts": 4538, + "src/main/ai-vault-search/session-search-live-transcript.test.ts": 83, + "src/main/ai-vault-search/session-search-merged-roots.test.ts": 111, + "src/main/ai-vault-search/session-search-message-rows.test.ts": 787, + "src/main/ai-vault-search/session-search-native-chat-indexing.test.ts": 211, + "src/main/ai-vault-search/session-search-opencode-decline.test.ts": 218, + "src/main/ai-vault-search/session-search-orphan-rows.test.ts": 179, + "src/main/ai-vault-search/session-search-paging.test.ts": 497, + "src/main/ai-vault-search/session-search-query-planner.test.ts": 14, + "src/main/ai-vault-search/session-search-read-decision.test.ts": 4, + "src/main/ai-vault-search/session-search-retention-delete.test.ts": 108, + "src/main/ai-vault-search/session-search-retention-policy.test.ts": 4, + "src/main/ai-vault-search/session-search-row-filter.test.ts": 194, + "src/main/ai-vault-search/session-search-row-identity.test.ts": 52, + "src/main/ai-vault-search/session-search-scan-roots.test.ts": 5, + "src/main/ai-vault-search/session-search-schema.test.ts": 322, + "src/main/ai-vault-search/session-search-sidebar-parity.test.ts": 213, + "src/main/ai-vault-search/session-search-snippet-marks.test.ts": 118, + "src/main/ai-vault-search/session-search-store-is-memory.test.ts": 305, + "src/main/ai-vault-search/session-search-synthetic-corpus.test.ts": 188, + "src/main/ai-vault-search/session-search-typo-policy.test.ts": 556, + "src/main/ai-vault-search/session-search-typo-scope.test.ts": 47, + "src/main/ai-vault/ai-vault-scan-cancellation.test.ts": 5, + "src/main/ai-vault/ai-vault-scan-coordinator.test.ts": 16, + "src/main/ai-vault/cached-session-list.test.ts": 10, + "src/main/ai-vault/claude-project-dir-encoding.test.ts": 10, + "src/main/ai-vault/codex-session-collection.test.ts": 32, + "src/main/ai-vault/codex-session-root-dedup.test.ts": 13, + "src/main/ai-vault/local-log-tail-reader.test.ts": 25, + "src/main/ai-vault/remote-session-parse-cache.test.ts": 26, + "src/main/ai-vault/remote-session-scan-batching.test.ts": 6, + "src/main/ai-vault/remote-session-scanner-omp-subagents.test.ts": 29, + "src/main/ai-vault/remote-session-scanner.test.ts": 37, + "src/main/ai-vault/remote-session-sidecar-observation.test.ts": 31, + "src/main/ai-vault/runtime-session-scanner.test.ts": 16, + "src/main/ai-vault/session-delete-target.test.ts": 20, + "src/main/ai-vault/session-delete.test.ts": 22, + "src/main/ai-vault/session-first-user-prompt-read.test.ts": 28, + "src/main/ai-vault/session-list-result-validation.test.ts": 27, + "src/main/ai-vault/session-list-results.test.ts": 12, + "src/main/ai-vault/session-newest-files.test.ts": 121, + "src/main/ai-vault/session-parse-cache-persistence.test.ts": 73, + "src/main/ai-vault/session-scanner-antigravity-parser.test.ts": 9, + "src/main/ai-vault/session-scanner-antigravity-source.test.ts": 57, + "src/main/ai-vault/session-scanner-background.test.ts": 8, + "src/main/ai-vault/session-scanner-claude-cwd-drift.test.ts": 23, + "src/main/ai-vault/session-scanner-claude-subagent-prune.test.ts": 21, + "src/main/ai-vault/session-scanner-claude-subagents.test.ts": 71, + "src/main/ai-vault/session-scanner-claude-title.test.ts": 32, + "src/main/ai-vault/session-scanner-claude-unicode-scope.test.ts": 43, + "src/main/ai-vault/session-scanner-cline-parser.test.ts": 32, + "src/main/ai-vault/session-scanner-codex-dual-root.test.ts": 51, + "src/main/ai-vault/session-scanner-codex-fast-path.test.ts": 180, + "src/main/ai-vault/session-scanner-codex-parser.test.ts": 47, + "src/main/ai-vault/session-scanner-codex-title-index.test.ts": 175, + "src/main/ai-vault/session-scanner-codex-tool-records.test.ts": 16, + "src/main/ai-vault/session-scanner-codex-workers.test.ts": 36, + "src/main/ai-vault/session-scanner-core-parser-wsl-stall.test.ts": 45, + "src/main/ai-vault/session-scanner-cursor-chat-meta.test.ts": 154, + "src/main/ai-vault/session-scanner-dedup-batches.test.ts": 228, + "src/main/ai-vault/session-scanner-devin-parser.test.ts": 13, + "src/main/ai-vault/session-scanner-directory-reader.test.ts": 15, + "src/main/ai-vault/session-scanner-discovery-wsl-gate.test.ts": 16, + "src/main/ai-vault/session-scanner-first-user-prompt.test.ts": 9, + "src/main/ai-vault/session-scanner-fs-import-guard.test.ts": 28, + "src/main/ai-vault/session-scanner-graph-parsers.test.ts": 6, + "src/main/ai-vault/session-scanner-grok-parser.test.ts": 26, + "src/main/ai-vault/session-scanner-grok-user-text.test.ts": 6, + "src/main/ai-vault/session-scanner-index-cache-wsl-stall.test.ts": 25, + "src/main/ai-vault/session-scanner-injected-title.test.ts": 29, + "src/main/ai-vault/session-scanner-jsonl-reader.test.ts": 84, + "src/main/ai-vault/session-scanner-kimi-index-cache.test.ts": 31, + "src/main/ai-vault/session-scanner-kimi-parser.test.ts": 153, + "src/main/ai-vault/session-scanner-omp-subagent-listing.test.ts": 24, + "src/main/ai-vault/session-scanner-omp-subagent-prune.test.ts": 38, + "src/main/ai-vault/session-scanner-omp-subagent-transcripts.test.ts": 15, + "src/main/ai-vault/session-scanner-opencode-parser.test.ts": 25, + "src/main/ai-vault/session-scanner-opencode-sources-wsl-stall.test.ts": 23, + "src/main/ai-vault/session-scanner-opencode-sources.test.ts": 9, + "src/main/ai-vault/session-scanner-opencode-sqlite-bounds.test.ts": 539, + "src/main/ai-vault/session-scanner-opencode-sqlite-coexistence.test.ts": 58, + "src/main/ai-vault/session-scanner-opencode-sqlite-open.test.ts": 4131, + "src/main/ai-vault/session-scanner-opencode-sqlite-paths.test.ts": 9, + "src/main/ai-vault/session-scanner-opencode-sqlite-worker-client.test.ts": 23, + "src/main/ai-vault/session-scanner-opencode-sqlite-worker-spawn.test.ts": 6, + "src/main/ai-vault/session-scanner-opencode-sqlite.test.ts": 225, + "src/main/ai-vault/session-scanner-parse-cache-agents.test.ts": 198, + "src/main/ai-vault/session-scanner-parse-cache.test.ts": 57, + "src/main/ai-vault/session-scanner-parse-wsl-stall.test.ts": 31, + "src/main/ai-vault/session-scanner-parser-stream-cleanup.test.ts": 21, + "src/main/ai-vault/session-scanner-preview-window-truncation.test.ts": 6, + "src/main/ai-vault/session-scanner-recoverable-empty.test.ts": 57, + "src/main/ai-vault/session-scanner-roots.test.ts": 7, + "src/main/ai-vault/session-scanner-scope.test.ts": 82, + "src/main/ai-vault/session-scanner-service-client.test.ts": 178, + "src/main/ai-vault/session-scanner-service-entry-path.test.ts": 5, + "src/main/ai-vault/session-scanner-service-entry.test.ts": 486, + "src/main/ai-vault/session-scanner-service-env.test.ts": 8, + "src/main/ai-vault/session-scanner-service-restart-policy.test.ts": 6, + "src/main/ai-vault/session-scanner-service-spawn.test.ts": 6, + "src/main/ai-vault/session-scanner-values.test.ts": 21, + "src/main/ai-vault/session-scanner-worker-client.test.ts": 17, + "src/main/ai-vault/session-scanner.test.ts": 109, + "src/main/ai-vault/session-sidecar-stat.test.ts": 9, + "src/main/ai-vault/session-title-file-reader-wsl-stall.test.ts": 22, + "src/main/ai-vault/session-title-file-reader.test.ts": 15, + "src/main/ai-vault/session-transcript-consumers.test.ts": 107, + "src/main/ai-vault/session-transcript-message-content.test.ts": 10, + "src/main/ai-vault/ssh-session-list.test.ts": 87, + "src/main/ai-vault/structured-session-ownership.test.ts": 18, + "src/main/amp/hook-service.test.ts": 13, + "src/main/antigravity/hook-service.test.ts": 61, + "src/main/antigravity/windows-hook-payload-delivery.test.ts": 10, + "src/main/app-icon.test.ts": 24, + "src/main/app-relaunch.test.ts": 7, + "src/main/appimage-runtime-identity.test.ts": 36, + "src/main/appkit-scene-mutation.test.ts": 9, + "src/main/artifacts/artifact-cloud-config.test.ts": 6, + "src/main/artifacts/artifact-cloud-recovery.test.ts": 132, + "src/main/artifacts/artifact-cloud-service-races.test.ts": 219, + "src/main/artifacts/artifact-cloud-service.test.ts": 725, + "src/main/artifacts/artifact-create-intent-store.test.ts": 339, + "src/main/artifacts/artifact-recovery-directory-fsync.test.ts": 14, + "src/main/artifacts/artifact-share-record-store.test.ts": 310, + "src/main/asar-transparent-fs.test.ts": 9, + "src/main/automations/automation-dispatch-host-fence.test.ts": 1300, + "src/main/automations/automation-owner-fencing.test.ts": 1874, + "src/main/automations/automation-owner-migration.test.ts": 17, + "src/main/automations/automation-run-terminal-surface.test.ts": 34, + "src/main/automations/automation-run-writer.test.ts": 7, + "src/main/automations/automation-skip-coalescing.test.ts": 1783, + "src/main/automations/automation-ssh-readoption-migration.test.ts": 9, + "src/main/automations/automation-update-host-retarget.test.ts": 1187, + "src/main/automations/automation-workspace-host-attribution.test.ts": 757, + "src/main/automations/external-automation-manager-cache.test.ts": 12, + "src/main/automations/external-automation-owner-guard.test.ts": 8, + "src/main/automations/external-automation-probe-scheduler.test.ts": 123, + "src/main/automations/external-job-run-sorting.test.ts": 170, + "src/main/automations/external-manager-scoped.test.ts": 23, + "src/main/automations/external-manager.test.ts": 15, + "src/main/automations/headless-workspace-create.test.ts": 9, + "src/main/automations/hermes-cron-output.test.ts": 73, + "src/main/automations/precheck-runner.test.ts": 1066, + "src/main/automations/refused-manual-run.test.ts": 2290, + "src/main/automations/retained-run-reconciliation.test.ts": 491, + "src/main/automations/run-completion-watcher.test.ts": 1046, + "src/main/automations/run-target-resolution.test.ts": 6, + "src/main/automations/runtime-terminal-run-observer.test.ts": 54, + "src/main/automations/service-precheck.test.ts": 931, + "src/main/automations/service.test.ts": 1218, + "src/main/azure-devops/azure-devops-api-request.test.ts": 61, + "src/main/azure-devops/client.test.ts": 84, + "src/main/azure-devops/pull-request-creation.test.ts": 74, + "src/main/azure-devops/pull-request-mappers.test.ts": 5, + "src/main/azure-devops/repository-ref.test.ts": 33, + "src/main/bitbucket/client.test.ts": 102, + "src/main/bitbucket/credential-connection.test.ts": 151, + "src/main/bitbucket/credential-store.test.ts": 146, + "src/main/bitbucket/pull-request-creation.test.ts": 70, + "src/main/bitbucket/pull-request-mappers.test.ts": 7, + "src/main/bitbucket/repository-ref.test.ts": 24, + "src/main/bitbucket/status-no-decrypt.test.ts": 171, + "src/main/browser/agent-browser-bridge-automation-visibility.test.ts": 91, + "src/main/browser/agent-browser-bridge-command-transport.test.ts": 102, + "src/main/browser/agent-browser-bridge-mouse-input.test.ts": 24, + "src/main/browser/agent-browser-bridge-navigation.test.ts": 156, + "src/main/browser/agent-browser-bridge-session-lifecycle.test.ts": 244, + "src/main/browser/agent-browser-bridge-tab-routing.test.ts": 89, + "src/main/browser/agent-browser-bridge-text-input.test.ts": 104, + "src/main/browser/agent-browser-orphan-sweep.test.ts": 14, + "src/main/browser/agent-browser-process-environment.test.ts": 9, + "src/main/browser/agent-browser-session-reset.test.ts": 9, + "src/main/browser/browser-certificate-trust-controller.test.ts": 186, + "src/main/browser/browser-clicked-link-routing.test.ts": 111, + "src/main/browser/browser-client-download-relay.test.ts": 39, + "src/main/browser/browser-client-download-routing.test.ts": 12, + "src/main/browser/browser-client-file-channel-negotiation.test.ts": 13, + "src/main/browser/browser-client-file-channel-reconnect.test.ts": 317, + "src/main/browser/browser-client-host-attach-request.test.ts": 13, + "src/main/browser/browser-client-host-authority-replacement.test.ts": 20, + "src/main/browser/browser-client-host-command-dispatcher.test.ts": 26, + "src/main/browser/browser-client-host-id.test.ts": 37, + "src/main/browser/browser-client-host-placement-preparation.test.ts": 17, + "src/main/browser/browser-client-host-published-url-wiring.test.ts": 451, + "src/main/browser/browser-client-host-reconciliation-command-order.test.ts": 19, + "src/main/browser/browser-client-network-route-registry.test.ts": 45, + "src/main/browser/browser-client-page-automation-runtime.test.ts": 15, + "src/main/browser/browser-client-page-command-executor-fencing.test.ts": 25, + "src/main/browser/browser-client-page-command-executor.test.ts": 33, + "src/main/browser/browser-client-page-command-integration.test.ts": 21, + "src/main/browser/browser-client-page-execution-host-supersession.test.ts": 12, + "src/main/browser/browser-client-page-inventory.test.ts": 68, + "src/main/browser/browser-client-page-metadata-transport.test.ts": 21, + "src/main/browser/browser-client-page-published-url.test.ts": 12, + "src/main/browser/browser-client-page-reconciliation-adapters.test.ts": 28, + "src/main/browser/browser-client-page-renderer-bridge.test.ts": 35, + "src/main/browser/browser-client-page-renderer-lifecycle.electron.test.ts": 1571, + "src/main/browser/browser-client-page-renderer-runtime.test.ts": 12, + "src/main/browser/browser-client-page-unavailability.test.ts": 13, + "src/main/browser/browser-client-page-upload-routing.test.ts": 60, + "src/main/browser/browser-client-route-cookie-import-partition-parity.test.ts": 20, + "src/main/browser/browser-client-route-cookie-import.test.ts": 14, + "src/main/browser/browser-client-upload-command.test.ts": 34, + "src/main/browser/browser-client-upload-staging.test.ts": 57, + "src/main/browser/browser-client-upload-transfer.test.ts": 3444, + "src/main/browser/browser-cookie-clear-preserve.test.ts": 71, + "src/main/browser/browser-cookie-clear-store-lifecycle.test.ts": 11, + "src/main/browser/browser-cookie-clear-store.test.ts": 25, + "src/main/browser/browser-cookie-import-app-bound-prefix.test.ts": 7, + "src/main/browser/browser-cookie-import-clear-atomicity.test.ts": 15, + "src/main/browser/browser-cookie-import-concurrency.test.ts": 113, + "src/main/browser/browser-cookie-import-google-exclusion.test.ts": 135, + "src/main/browser/browser-cookie-import-partition-fidelity.test.ts": 502, + "src/main/browser/browser-cookie-import-partition-reland.electron.test.ts": 2913, + "src/main/browser/browser-cookie-import-partition-rollback.electron.test.ts": 4236, + "src/main/browser/browser-cookie-import-partition-success.electron.test.ts": 513, + "src/main/browser/browser-cookie-import-partition.electron.test.ts": 606, + "src/main/browser/browser-cookie-import-plan-writes.test.ts": 19, + "src/main/browser/browser-cookie-import-policy.test.ts": 127, + "src/main/browser/browser-cookie-import-replace-partition-rollback.electron.test.ts": 1412, + "src/main/browser/browser-cookie-import-replacement.test.ts": 72, + "src/main/browser/browser-cookie-import-route-partition-staging.test.ts": 51, + "src/main/browser/browser-cookie-import-scope.test.ts": 72, + "src/main/browser/browser-cookie-import-undecryptable.test.ts": 285, + "src/main/browser/browser-cookie-import-validated-partition.electron.test.ts": 594, + "src/main/browser/browser-cookie-import-write.test.ts": 11, + "src/main/browser/browser-cookie-import.comet.test.ts": 427, + "src/main/browser/browser-cookie-import.helium.test.ts": 358, + "src/main/browser/browser-cookie-import.test.ts": 1644, + "src/main/browser/browser-cookie-registrable-family.test.ts": 19, + "src/main/browser/browser-cookie-samesite.electron.test.ts": 714, + "src/main/browser/browser-cookie-source-partition.test.ts": 8, + "src/main/browser/browser-cookie-validation.test.ts": 7, + "src/main/browser/browser-download-destination.test.ts": 15, + "src/main/browser/browser-execution-host-storage-identity.test.ts": 10, + "src/main/browser/browser-google-auth-ua.test.ts": 7, + "src/main/browser/browser-grab-payload.test.ts": 13, + "src/main/browser/browser-guest-shortcut-forwarding.test.ts": 31, + "src/main/browser/browser-guest-wheel-zoom-scroll.test.ts": 6, + "src/main/browser/browser-host-client-identity.test.ts": 30, + "src/main/browser/browser-host-lease-reconnect-delay.test.ts": 6, + "src/main/browser/browser-manager-annotation-bridge.test.ts": 16, + "src/main/browser/browser-manager-auth-user-agent.test.ts": 19, + "src/main/browser/browser-manager-client-hosted-downloads.test.ts": 135, + "src/main/browser/browser-manager-downloads.test.ts": 40, + "src/main/browser/browser-manager-grab-capture.test.ts": 13, + "src/main/browser/browser-manager-grab-mode.test.ts": 16, + "src/main/browser/browser-manager-grab-selection.test.ts": 37, + "src/main/browser/browser-manager-grab-shortcuts.test.ts": 14, + "src/main/browser/browser-manager-guest-lifecycle.test.ts": 33, + "src/main/browser/browser-manager-guest-policy-profile.test.ts": 11, + "src/main/browser/browser-manager-guest-shortcuts.test.ts": 26, + "src/main/browser/browser-manager-guest-visibility.test.ts": 16, + "src/main/browser/browser-manager-load-failure-replay.test.ts": 29, + "src/main/browser/browser-manager-popup-child.test.ts": 19, + "src/main/browser/browser-manager-popup-routing.test.ts": 77, + "src/main/browser/browser-manager-viewport-override.test.ts": 45, + "src/main/browser/browser-manager-viewport-partial-failure.test.ts": 13, + "src/main/browser/browser-network-deferred-socket.test.ts": 6, + "src/main/browser/browser-network-execution-route.test.ts": 14, + "src/main/browser/browser-network-tunnel-client-memory-budget.test.ts": 13, + "src/main/browser/browser-network-tunnel-client.test.ts": 36, + "src/main/browser/browser-network-tunnel-conformance.test.ts": 56, + "src/main/browser/browser-network-tunnel-outbound-memory-budget.test.ts": 7, + "src/main/browser/browser-network-tunnel-session-aggregate-memory.test.ts": 6, + "src/main/browser/browser-network-tunnel-session-stream-scoped-failure.test.ts": 26, + "src/main/browser/browser-network-tunnel-session.test.ts": 57, + "src/main/browser/browser-page-initiated-tab-budget.test.ts": 5, + "src/main/browser/browser-route-dns-prefetch.electron.test.ts": 4337, + "src/main/browser/browser-route-guest-popups.test.ts": 14, + "src/main/browser/browser-route-h3-egress.electron.test.ts": 13849, + "src/main/browser/browser-route-identity.test.ts": 8, + "src/main/browser/browser-route-partition-binding-capacity.test.ts": 27, + "src/main/browser/browser-route-partition-binding-store.test.ts": 42, + "src/main/browser/browser-route-partition-migration.test.ts": 261, + "src/main/browser/browser-route-partition-stability.test.ts": 30, + "src/main/browser/browser-route-partition-storage-lifecycle.test.ts": 33, + "src/main/browser/browser-route-partition-storage-retirement.test.ts": 14, + "src/main/browser/browser-route-partition-storage-runtime.test.ts": 10, + "src/main/browser/browser-route-persisted-worker-egress.electron.test.ts": 1471, + "src/main/browser/browser-route-prepared-page-rekey.test.ts": 9, + "src/main/browser/browser-route-session-registry.test.ts": 30, + "src/main/browser/browser-route-tcp-egress.electron.test.ts": 1082, + "src/main/browser/browser-route-webcontents-registry.test.ts": 43, + "src/main/browser/browser-route-webrtc-egress.electron.test.ts": 8203, + "src/main/browser/browser-screencast-lifecycle.test.ts": 16, + "src/main/browser/browser-screencast-snapshot-scaling.test.ts": 116, + "src/main/browser/browser-screencast-stream.test.ts": 549, + "src/main/browser/browser-session-cookie-staging.scoped.test.ts": 105, + "src/main/browser/browser-session-cookie-staging.test.ts": 8, + "src/main/browser/browser-session-partition-policies.test.ts": 36, + "src/main/browser/browser-session-partition-proxy-install.test.ts": 222, + "src/main/browser/browser-session-proxy.test.ts": 171, + "src/main/browser/browser-session-registry.persistence.test.ts": 550, + "src/main/browser/browser-session-registry.test.ts": 497, + "src/main/browser/browser-session-startup.test.ts": 33, + "src/main/browser/browser-session-ua-wire-identity.electron.test.ts": 2126, + "src/main/browser/browser-text-insertion.test.ts": 19, + "src/main/browser/browser-viewport-user-agent.test.ts": 9, + "src/main/browser/browser-webauthn-access.test.ts": 7, + "src/main/browser/browser-webauthn-account-picker.test.ts": 15, + "src/main/browser/browser-webauthn-profile-delete.test.ts": 64, + "src/main/browser/cdp-bridge-integration.test.ts": 3216, + "src/main/browser/cdp-bridge-state.test.ts": 26, + "src/main/browser/cdp-print-to-pdf.test.ts": 22, + "src/main/browser/cdp-screenshot.test.ts": 16, + "src/main/browser/cdp-ws-proxy-focus-replay.test.ts": 199, + "src/main/browser/cdp-ws-proxy.test.ts": 2405, + "src/main/browser/chromium-cookie-snapshot.test.ts": 279, + "src/main/browser/client-route-cookie-import-source-store.test.ts": 51, + "src/main/browser/doc-preview-download-block-notice.test.ts": 15, + "src/main/browser/doc-preview-failure-notice.test.ts": 7, + "src/main/browser/doc-preview-file-reader.test.ts": 31, + "src/main/browser/doc-preview-grant-registry.test.ts": 12, + "src/main/browser/doc-preview-guest-policy.test.ts": 46, + "src/main/browser/doc-preview-protocol.test.ts": 48, + "src/main/browser/electron-debugger-lease.test.ts": 4, + "src/main/browser/electron-probe-display-launch.test.ts": 8, + "src/main/browser/execution-route-socket-duplex.test.ts": 42, + "src/main/browser/grab-guest-script.test.ts": 29, + "src/main/browser/local-ssh-browser-partition-identity.test.ts": 11, + "src/main/browser/local-ssh-browser-partitions.probe-cache.test.ts": 7, + "src/main/browser/local-ssh-browser-route.test.ts": 40, + "src/main/browser/offscreen-browser-backend-lifecycle.test.ts": 67, + "src/main/browser/offscreen-browser-backend.web-preferences.test.ts": 7, + "src/main/browser/paired-runtime-browser-client-host-composition.test.ts": 51, + "src/main/browser/paired-runtime-browser-client-host-identity-wiring.test.ts": 732, + "src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts": 274, + "src/main/browser/paired-runtime-browser-client-host-registry.test.ts": 13, + "src/main/browser/paired-runtime-browser-client-host-route-identity.test.ts": 60, + "src/main/browser/paired-runtime-browser-client-host-runtime.test.ts": 7, + "src/main/browser/paired-runtime-browser-client-host.test.ts": 95, + "src/main/browser/paired-runtime-browser-host-command-admission.test.ts": 24, + "src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts": 708, + "src/main/browser/paired-runtime-browser-host-lease.test.ts": 692, + "src/main/browser/paired-runtime-browser-host-reconciliation-negotiation.test.ts": 66, + "src/main/browser/paired-runtime-browser-network-route.test.ts": 1007, + "src/main/browser/popup-origin-bar-window.test.ts": 19, + "src/main/browser/remote-browser-socks-buffering.test.ts": 1148, + "src/main/browser/remote-browser-socks-server.test.ts": 296, + "src/main/browser/snapshot-engine.test.ts": 13, + "src/main/browser/ssh-browser-network-execution-route.test.ts": 175, + "src/main/browser/system-ssh-socks-client-socket.test.ts": 16, + "src/main/browser/wsl-browser-network-execution-route.test.ts": 128, + "src/main/browser/wsl-browser-network-relay-launch.test.ts": 23, + "src/main/claude-accounts/claude-account-service-account-selection.test.ts": 134, + "src/main/claude-accounts/claude-account-service-add-account.test.ts": 91, + "src/main/claude-accounts/claude-account-service-api-parity.test.ts": 65, + "src/main/claude-accounts/claude-account-service-config-dir-capture.test.ts": 157, + "src/main/claude-accounts/claude-account-service-credential-capture.test.ts": 94, + "src/main/claude-accounts/claude-account-service-login-process.test.ts": 480, + "src/main/claude-accounts/claude-account-service-reauth-rollback.test.ts": 193, + "src/main/claude-accounts/claude-duplicate-account.test.ts": 4, + "src/main/claude-accounts/claude-login-completion.oracle.test.ts": 105, + "src/main/claude-accounts/claude-structured-auth-policy.test.ts": 8, + "src/main/claude-accounts/claude-windows-interactive-login.test.ts": 75, + "src/main/claude-accounts/keychain.test.ts": 13, + "src/main/claude-accounts/live-pty-gate.test.ts": 13, + "src/main/claude-accounts/oauth-refresh.test.ts": 20, + "src/main/claude-accounts/runtime-auth-service-account-switching.test.ts": 147, + "src/main/claude-accounts/runtime-auth-service-deselect-restore.test.ts": 284, + "src/main/claude-accounts/runtime-auth-service-keychain-snapshots.test.ts": 431, + "src/main/claude-accounts/runtime-auth-service-launch-refresh.test.ts": 159, + "src/main/claude-accounts/runtime-auth-service-materialization.test.ts": 226, + "src/main/claude-accounts/runtime-auth-service-readback-identity.test.ts": 298, + "src/main/claude-accounts/runtime-auth-service-snapshot-validation.test.ts": 319, + "src/main/claude-accounts/runtime-auth-service-wsl-runtime.test.ts": 197, + "src/main/claude-accounts/runtime-selection.test.ts": 4, + "src/main/claude-accounts/windows-command-invocation.test.ts": 8, + "src/main/claude-usage/claude-model-pricing.test.ts": 12, + "src/main/claude-usage/claude-usage-report-aggregation.test.ts": 6, + "src/main/claude-usage/scanner-large-directory.test.ts": 530, + "src/main/claude-usage/scanner-scan.test.ts": 114, + "src/main/claude-usage/scanner.test.ts": 31, + "src/main/claude-usage/store.test.ts": 35, + "src/main/claude-usage/transcript-record-parser-prefilter.test.ts": 5, + "src/main/claude-usage/worktree-attribution-scaling.test.ts": 13, + "src/main/claude/claude-agent-sdk-contract-pins.test.ts": 2683, + "src/main/claude/claude-agent-sdk-control-requests.test.ts": 10, + "src/main/claude/claude-agent-sdk-exit-proof-identity.test.ts": 7, + "src/main/claude/claude-agent-sdk-exit-proof.test.ts": 12842, + "src/main/claude/claude-agent-sdk-import-boundary.test.ts": 951, + "src/main/claude/claude-agent-sdk-process-spawn.test.ts": 13, + "src/main/claude/claude-agent-sdk-root-kill-fallback.test.ts": 15, + "src/main/claude/claude-agent-sdk-user-message-queue.test.ts": 109, + "src/main/claude/claude-background-task-resume.test.ts": 14, + "src/main/claude/claude-background-task-tracker.test.ts": 93, + "src/main/claude/claude-child-process-environment.test.ts": 4, + "src/main/claude/claude-command-lifecycle-frames.test.ts": 7, + "src/main/claude/claude-config-dir-pin.test.ts": 6, + "src/main/claude/claude-descendant-escalation-boundary.test.ts": 462, + "src/main/claude/claude-slash-command-catalog.test.ts": 10, + "src/main/claude/claude-stream-json-connection-close.test.ts": 140, + "src/main/claude/claude-stream-json-connection.test.ts": 22607, + "src/main/claude/claude-streamed-text-checkpoints.test.ts": 9, + "src/main/claude/claude-structured-auth-parity.test.ts": 100, + "src/main/claude/claude-structured-compaction.test.ts": 10, + "src/main/claude/claude-structured-content-parts.test.ts": 18, + "src/main/claude/claude-structured-control-actions.test.ts": 12, + "src/main/claude/claude-structured-dispatch-admission.test.ts": 15, + "src/main/claude/claude-structured-dispatch-content.test.ts": 10, + "src/main/claude/claude-structured-dispatch.test.ts": 1411, + "src/main/claude/claude-structured-effort-reporting.test.ts": 27, + "src/main/claude/claude-structured-inbound-control.test.ts": 15, + "src/main/claude/claude-structured-journal-translation-subagents.test.ts": 23, + "src/main/claude/claude-structured-journal-translation-turn-timing.test.ts": 22, + "src/main/claude/claude-structured-journal-translation.test.ts": 63, + "src/main/claude/claude-structured-launch-resolution.test.ts": 28, + "src/main/claude/claude-structured-location-support.test.ts": 6, + "src/main/claude/claude-structured-model-confirmation.test.ts": 18, + "src/main/claude/claude-structured-option-confirmation.test.ts": 16, + "src/main/claude/claude-structured-options.test.ts": 8, + "src/main/claude/claude-structured-owner-identity.test.ts": 10, + "src/main/claude/claude-structured-prompt-items.test.ts": 11, + "src/main/claude/claude-structured-provider-fallback.test.ts": 25, + "src/main/claude/claude-structured-rewind.test.ts": 25, + "src/main/claude/claude-structured-session-acquisition-processless.test.ts": 8, + "src/main/claude/claude-structured-session-adapter-turns.test.ts": 25, + "src/main/claude/claude-structured-session-adapter.test.ts": 63, + "src/main/claude/claude-structured-session-close.test.ts": 26, + "src/main/claude/claude-structured-session-commands.test.ts": 10, + "src/main/claude/claude-structured-session-recovery.test.ts": 47, + "src/main/claude/claude-subagent-group-row.test.ts": 6, + "src/main/claude/claude-subagent-id-aliases.test.ts": 7, + "src/main/claude/claude-subagent-roster.test.ts": 22, + "src/main/claude/claude-subagent-task-frames.test.ts": 12, + "src/main/claude/claude-transcript-rewind-proof.test.ts": 10, + "src/main/claude/claude-tui-exit.test.ts": 18, + "src/main/claude/claude-tui-resume-launch.test.ts": 10, + "src/main/claude/claude-tui-resume-proof.test.ts": 9, + "src/main/claude/compact-status-registration.test.ts": 27, + "src/main/claude/hook-service.test.ts": 58, + "src/main/claude/statusline-script.test.ts": 186, + "src/main/cli/appimage-extracted-root.test.ts": 148, + "src/main/cli/appimage-extraction-pruning.test.ts": 37, + "src/main/cli/appimage-payload-removal.reentrancy.test.ts": 12, + "src/main/cli/appimage-payload-removal.test.ts": 13, + "src/main/cli/appimage-registration-lock.test.ts": 31, + "src/main/cli/appimage-stable-launcher.test.ts": 158, + "src/main/cli/cli-command-installation-races.test.ts": 78, + "src/main/cli/cli-installer-appimage-ownership.test.ts": 104, + "src/main/cli/cli-installer-appimage-removal.test.ts": 93, + "src/main/cli/cli-installer-command-conflicts.test.ts": 65, + "src/main/cli/cli-installer-macos-command-path.test.ts": 84, + "src/main/cli/cli-installer-windows-path.test.ts": 59, + "src/main/cli/cli-installer.test.ts": 153, + "src/main/cli/cli-privileged-processes.test.ts": 10, + "src/main/cli/keyed-promise-queue.test.ts": 8, + "src/main/cli/legacy-appimage-cli-wrapper.test.ts": 5, + "src/main/cli/linux-bare-orca-dispatcher.test.ts": 51, + "src/main/cli/linux-terminal-orca-cli-shim.test.ts": 75, + "src/main/cli/orca-cli-child-path.test.ts": 14, + "src/main/cli/packaged-cli-assets.test.ts": 492, + "src/main/cli/windows-launcher-asset.test.ts": 7, + "src/main/cli/windows-user-path-registry.test.ts": 10, + "src/main/cli/wsl-cli-installer.test.ts": 52, + "src/main/cli/wsl-cli-powershell-boundary.test.ts": 6, + "src/main/cli/wsl-cli-registration-operation.test.ts": 63, + "src/main/cli/wsl-cli-registration-reconciliation.test.ts": 13, + "src/main/cli/wsl-cli-registration-registry.test.ts": 84, + "src/main/codex-accounts/codex-account-identity-api-key-guard.test.ts": 7, + "src/main/codex-accounts/codex-auth-workspace-identity.test.ts": 15, + "src/main/codex-accounts/codex-credential-absence-grace.test.ts": 9, + "src/main/codex-accounts/codex-windows-interactive-login.test.ts": 234, + "src/main/codex-accounts/fs-utils.test.ts": 69, + "src/main/codex-accounts/host-codex-managed-home-ownership.test.ts": 12, + "src/main/codex-accounts/legacy-shared-auth-migration.test.ts": 62, + "src/main/codex-accounts/legacy-shared-config-compatibility.test.ts": 20, + "src/main/codex-accounts/legacy-wsl-runtime-auth-drain-apply-script.test.ts": 11759, + "src/main/codex-accounts/legacy-wsl-runtime-auth-drain-rollback-script.test.ts": 400, + "src/main/codex-accounts/legacy-wsl-runtime-auth-drain.test.ts": 23, + "src/main/codex-accounts/managed-codex-auth-readiness.test.ts": 36, + "src/main/codex-accounts/runtime-home-legacy-migration.test.ts": 381, + "src/main/codex-accounts/runtime-home-managed-auth-recovery.test.ts": 217, + "src/main/codex-accounts/runtime-home-mirrored-status-home.test.ts": 163, + "src/main/codex-accounts/runtime-home-per-account-homes.test.ts": 440, + "src/main/codex-accounts/runtime-home-real-home-lane-routing.test.ts": 278, + "src/main/codex-accounts/runtime-home-resume-selection-gate.test.ts": 119, + "src/main/codex-accounts/runtime-home-retained-auth-provenance.test.ts": 657, + "src/main/codex-accounts/runtime-home-service-per-account-migration.test.ts": 278, + "src/main/codex-accounts/runtime-home-session-migration-pass.test.ts": 282, + "src/main/codex-accounts/runtime-home-system-default-mirror-readback.test.ts": 709, + "src/main/codex-accounts/runtime-home-system-default-snapshot.test.ts": 618, + "src/main/codex-accounts/runtime-home-system-resource-materialization.test.ts": 216, + "src/main/codex-accounts/runtime-home-windows-profile-ownership.test.ts": 3, + "src/main/codex-accounts/runtime-home-wsl-managed-accounts.test.ts": 742, + "src/main/codex-accounts/runtime-home-wsl-session-bridge.test.ts": 393, + "src/main/codex-accounts/runtime-home-wsl-system-default.test.ts": 408, + "src/main/codex-accounts/runtime-selection.test.ts": 6, + "src/main/codex-accounts/service-account-add-login.test.ts": 289, + "src/main/codex-accounts/service-account-selection-and-removal.test.ts": 614, + "src/main/codex-accounts/service-add-account-from-home.test.ts": 177, + "src/main/codex-accounts/service-login-process-teardown.test.ts": 166, + "src/main/codex-accounts/service-managed-home-removal-retries.test.ts": 165, + "src/main/codex-accounts/service-quota-refresh-decoupling.test.ts": 174, + "src/main/codex-accounts/service-reauthenticate-activation.test.ts": 129, + "src/main/codex-accounts/service-reset-credit-durability.test.ts": 449, + "src/main/codex-accounts/service-reset-credit-home-ownership.test.ts": 175, + "src/main/codex-accounts/service-reset-credit-target-routing.test.ts": 339, + "src/main/codex-accounts/service-system-default-identity.test.ts": 207, + "src/main/codex-accounts/service-wsl-accounts.test.ts": 298, + "src/main/codex-accounts/service.test.ts": 181, + "src/main/codex-accounts/sta-4422-transient-ownership-error.test.ts": 209, + "src/main/codex-accounts/sta-4734-login-rollback-deletes-authenticated-home.test.ts": 376, + "src/main/codex-accounts/sta-4735-unreadable-host-lane-writes.test.ts": 145, + "src/main/codex-accounts/wsl-codex-command.test.ts": 8, + "src/main/codex-cli/codex-home-process-lock.test.ts": 15, + "src/main/codex-cli/command.test.ts": 39, + "src/main/codex-usage/scanner-large-directory.test.ts": 1093, + "src/main/codex-usage/scanner-paths.test.ts": 64, + "src/main/codex-usage/scanner.test.ts": 10, + "src/main/codex-usage/store-automation-usage.test.ts": 12, + "src/main/codex-usage/store-model-pricing.test.ts": 17, + "src/main/codex-usage/store-orca-scope.test.ts": 17, + "src/main/codex-usage/store-persistence.test.ts": 20, + "src/main/codex-usage/store-snapshot.benchmark.test.ts": 152, + "src/main/codex/codex-account-session-bridge.test.ts": 47, + "src/main/codex/codex-ai-vault-session-resume.test.ts": 14, + "src/main/codex/codex-app-server-capability-cache.test.ts": 11, + "src/main/codex/codex-app-server-client.test.ts": 1418, + "src/main/codex/codex-app-server-connection.test.ts": 5017, + "src/main/codex/codex-app-server-posix-supervisor.test.ts": 9, + "src/main/codex/codex-app-server-process-teardown.test.ts": 26, + "src/main/codex/codex-app-server-session.test.ts": 116, + "src/main/codex/codex-app-server-teardown.integration.test.ts": 17565, + "src/main/codex/codex-background-command-tracker.test.ts": 19, + "src/main/codex/codex-background-task-tracker.test.ts": 38, + "src/main/codex/codex-config-mirror.test.ts": 96, + "src/main/codex/codex-config-settings-removal.test.ts": 5, + "src/main/codex/codex-goal-journal-rows.test.ts": 14, + "src/main/codex/codex-home-paths.test.ts": 31, + "src/main/codex/codex-hook-legacy-profile-block.test.ts": 8, + "src/main/codex/codex-hook-trust-grant.test.ts": 241, + "src/main/codex/codex-hooks-read-denial-followup.test.ts": 116, + "src/main/codex/codex-legacy-session-resume.test.ts": 69, + "src/main/codex/codex-model-provider-config.test.ts": 9, + "src/main/codex/codex-notice-item-translation.test.ts": 13, + "src/main/codex/codex-pane-account-registry-mutations.test.ts": 14, + "src/main/codex/codex-pane-launch-account.test.ts": 8, + "src/main/codex/codex-persistent-command-retention.test.ts": 226, + "src/main/codex/codex-real-home-hook-install.test.ts": 70, + "src/main/codex/codex-real-home-path.test.ts": 7, + "src/main/codex/codex-requested-close-turn-timing.test.ts": 15, + "src/main/codex/codex-resume-process-proof.test.ts": 20, + "src/main/codex/codex-rollout-session-meta.test.ts": 21, + "src/main/codex/codex-server-request-disposition.test.ts": 21, + "src/main/codex/codex-session-backfill-marker.test.ts": 29, + "src/main/codex/codex-session-backfill-scan-dates.test.ts": 27, + "src/main/codex/codex-session-backfill.test.ts": 408, + "src/main/codex/codex-session-bridge.test.ts": 66, + "src/main/codex/codex-session-index-heal-state.test.ts": 369, + "src/main/codex/codex-session-index-heal.test.ts": 1379, + "src/main/codex/codex-session-migration-recent-exits.test.ts": 13, + "src/main/codex/codex-session-migration-scheduler.test.ts": 369, + "src/main/codex/codex-session-resume-home.test.ts": 22, + "src/main/codex/codex-session-resume-preparation.test.ts": 13, + "src/main/codex/codex-session-resume-wrong-account.test.ts": 24, + "src/main/codex/codex-session-source-home.test.ts": 5, + "src/main/codex/codex-stale-pane-accounts.test.ts": 95, + "src/main/codex/codex-state-db-backfill-recovery.test.ts": 147, + "src/main/codex/codex-state-db.test.ts": 27, + "src/main/codex/codex-structured-acquisition-exit-proof.test.ts": 11, + "src/main/codex/codex-structured-app-server-args.test.ts": 7, + "src/main/codex/codex-structured-child-environment.test.ts": 8, + "src/main/codex/codex-structured-item-translation.test.ts": 50, + "src/main/codex/codex-structured-journal-compactions.test.ts": 16, + "src/main/codex/codex-structured-journal-goal-admission.test.ts": 33, + "src/main/codex/codex-structured-journal-goal-resume.test.ts": 125, + "src/main/codex/codex-structured-journal-goal-rows.test.ts": 16, + "src/main/codex/codex-structured-journal-translation-settlement.test.ts": 843, + "src/main/codex/codex-structured-journal-translation-streams.test.ts": 85, + "src/main/codex/codex-structured-journal-translation-subagents.test.ts": 26, + "src/main/codex/codex-structured-journal-translation-turn-lifecycle.test.ts": 60, + "src/main/codex/codex-structured-journal-translation-turn-state.test.ts": 12, + "src/main/codex/codex-structured-journal-translation.test.ts": 96, + "src/main/codex/codex-structured-launch-resolution.test.ts": 17, + "src/main/codex/codex-structured-location-support.test.ts": 3, + "src/main/codex/codex-structured-owner-identity.test.ts": 9, + "src/main/codex/codex-structured-prompt-items.test.ts": 39, + "src/main/codex/codex-structured-prompt-replies.test.ts": 40, + "src/main/codex/codex-structured-rewind.test.ts": 34, + "src/main/codex/codex-structured-session-adapter-lifecycle.test.ts": 19, + "src/main/codex/codex-structured-session-adapter.test.ts": 37, + "src/main/codex/codex-structured-session-background-tasks.test.ts": 76, + "src/main/codex/codex-structured-session-cancel.test.ts": 164, + "src/main/codex/codex-structured-session-close.test.ts": 20, + "src/main/codex/codex-structured-session-options.test.ts": 12, + "src/main/codex/codex-structured-session-shutdown.test.ts": 62, + "src/main/codex/codex-structured-thread-open.test.ts": 19, + "src/main/codex/codex-structured-turn-processes.integration.test.ts": 85, + "src/main/codex/codex-subagent-execution-projection.test.ts": 16, + "src/main/codex/codex-subagent-executions.test.ts": 21, + "src/main/codex/codex-subagent-roster.test.ts": 40, + "src/main/codex/codex-tool-identity-translation.test.ts": 14, + "src/main/codex/codex-trust-config-concurrent-launch.test.ts": 70, + "src/main/codex/codex-trust-config-mutation-queue.test.ts": 10, + "src/main/codex/codex-trust-config-rollback.test.ts": 16, + "src/main/codex/codex-trust-grant-host.test.ts": 12, + "src/main/codex/codex-trust-grant-ledger.test.ts": 8, + "src/main/codex/codex-trust-grant-main-thread-boundary.test.ts": 15, + "src/main/codex/codex-trust-grant-telemetry.test.ts": 8, + "src/main/codex/codex-tui-rollout-proof.test.ts": 33, + "src/main/codex/codex-turn-ordinals.test.ts": 45, + "src/main/codex/codex-user-hook-trust-rebase-client.test.ts": 13, + "src/main/codex/codex-user-hook-trust-rebase.test.ts": 17, + "src/main/codex/codex-wsl-hook-install-plan.test.ts": 11, + "src/main/codex/config-plugin-registration-promotion.test.ts": 193, + "src/main/codex/config-settings-baseline-upgrade.test.ts": 61, + "src/main/codex/config-settings-promotion.test.ts": 227, + "src/main/codex/config-sync-stall.test.ts": 47, + "src/main/codex/config-toml-hook-trust-scaling.test.ts": 38, + "src/main/codex/config-toml-trust-api-parity.test.ts": 13, + "src/main/codex/config-toml-trust-hash.test.ts": 7, + "src/main/codex/config-toml-trust-hook-read.test.ts": 16, + "src/main/codex/config-toml-trust-hook-removal.test.ts": 26, + "src/main/codex/config-toml-trust-hook-upsert.test.ts": 44, + "src/main/codex/config-toml-trust-key.test.ts": 10, + "src/main/codex/config-toml-trust-paths.test.ts": 10, + "src/main/codex/config-toml-trust-project.test.ts": 27, + "src/main/codex/hook-service-concurrent-launch-install.test.ts": 618, + "src/main/codex/hook-service-legacy-cleanup.test.ts": 601, + "src/main/codex/hook-service-managed-install.test.ts": 99, + "src/main/codex/hook-service-runtime-trust-repair.test.ts": 117, + "src/main/codex/hook-service-trust-grant.test.ts": 123, + "src/main/codex/hook-service-user-hook-mirroring.test.ts": 186, + "src/main/codex/hook-service-wsl-runtime.test.ts": 113, + "src/main/codex/hook-trust-promotion.test.ts": 225, + "src/main/codex/managed-home-shell-preflight.test.ts": 28, + "src/main/codex/retained-codex-hook-state.test.ts": 8, + "src/main/codex/sta-4735-hook-trust-provenance-overwrite.test.ts": 12, + "src/main/codex/sta-4737-unreadable-is-not-absent.test.ts": 58, + "src/main/codex/sta-4823-shared-state-survives-a-failed-read.test.ts": 94, + "src/main/codex/wsl-codex-session-bridge.test.ts": 82, + "src/main/command-code/hook-service.test.ts": 239, + "src/main/computer/computer-action-verification-normalization.test.ts": 4, + "src/main/computer/computer-provider-lifecycle.test.ts": 5, + "src/main/computer/computer-provider-unavailable-message.test.ts": 3, + "src/main/computer/computer-sidecar-diagnostics.test.ts": 8, + "src/main/computer/desktop-script-provider-action-errors.test.ts": 68, + "src/main/computer/desktop-script-provider-actions.test.ts": 131, + "src/main/computer/desktop-script-provider-bridge.test.ts": 6, + "src/main/computer/desktop-script-provider-cache-lifecycle.test.ts": 96, + "src/main/computer/desktop-script-provider-cache.test.ts": 115, + "src/main/computer/desktop-script-provider-client.test.ts": 154, + "src/main/computer/desktop-script-provider-errors.test.ts": 161, + "src/main/computer/desktop-script-provider-paste-validation.test.ts": 105, + "src/main/computer/desktop-script-provider-runtime-host-routing.test.ts": 44, + "src/main/computer/desktop-script-runtime-host.test.ts": 106, + "src/main/computer/desktop-script-serve-channel.test.ts": 9, + "src/main/computer/macos-computer-use-permission-status.test.ts": 38, + "src/main/computer/macos-computer-use-permissions.test.ts": 24, + "src/main/computer/macos-native-provider-client.test.ts": 557, + "src/main/computer/macos-native-provider-paste-validation.test.ts": 99, + "src/main/computer/macos-native-provider-socket.test.ts": 14, + "src/main/computer/sidecar-client.test.ts": 28, + "src/main/computer/windows-powershell-execution-policy.test.ts": 7, + "src/main/copilot/hook-service.test.ts": 83, + "src/main/crash-reporting/crash-breadcrumb-renderer-attribution.test.ts": 29, + "src/main/crash-reporting/crash-breadcrumb-store-leak.test.ts": 60, + "src/main/crash-reporting/crash-breadcrumb-store-orphan-cleanup.test.ts": 45, + "src/main/crash-reporting/crash-breadcrumb-store.test.ts": 34, + "src/main/crash-reporting/crash-report-copy-text.test.ts": 8, + "src/main/crash-reporting/crash-report-store.test.ts": 351, + "src/main/crash-reporting/crashpad-capture.test.ts": 60, + "src/main/crash-reporting/durable-crash-breadcrumb.test.ts": 11, + "src/main/crash-reporting/expected-teardown-state.test.ts": 9, + "src/main/crash-reporting/gpu-crash-diagnostics.test.ts": 14, + "src/main/crash-reporting/gpu-crash-fallback-decision.test.ts": 13, + "src/main/crash-reporting/gpu-crash-fallback-field-sessions.test.ts": 9, + "src/main/crash-reporting/gpu-fallback-engagement.test.ts": 14, + "src/main/crash-reporting/gpu-fallback-recovered-launch.test.ts": 10, + "src/main/crash-reporting/gpu-fallback-restart-prompt.test.ts": 8, + "src/main/crash-reporting/main-process-lifecycle-identity.test.ts": 6, + "src/main/crash-reporting/minidump-crash-signature.test.ts": 30, + "src/main/crash-reporting/pre-gone-host-memory.test.ts": 18, + "src/main/crash-reporting/process-gone-classification.test.ts": 12, + "src/main/crash-reporting/process-gone-dedupe.test.ts": 6, + "src/main/crash-reporting/process-gone-diagnostics.test.ts": 19, + "src/main/crash-reporting/process-gone-killed-one-ordering.test.ts": 40, + "src/main/crash-reporting/process-gone-recorder.test.ts": 599, + "src/main/crash-reporting/process-gone-sibling-correlation.test.ts": 514, + "src/main/crash-reporting/renderer-recovery-circuit-breaker.test.ts": 10, + "src/main/crash-reporting/self-initiated-tree-kill-log.test.ts": 26, + "src/main/crash-reporting/suppressed-process-gone-breadcrumb.test.ts": 5, + "src/main/cursor/hook-service.test.ts": 173, + "src/main/daemon/agent-startup-prompt-latency.node-pty.test.ts": 1916, + "src/main/daemon/bash-prompt-command-composition.test.ts": 3541, + "src/main/daemon/client.test.ts": 392, + "src/main/daemon/cold-restore-payload-cache.test.ts": 9, + "src/main/daemon/daemon-adoption-telemetry-event.test.ts": 18, + "src/main/daemon/daemon-audit-eligibility-event.test.ts": 30, + "src/main/daemon/daemon-authenticated-client-activity.test.ts": 16, + "src/main/daemon/daemon-background-transient-facts.test.ts": 12, + "src/main/daemon/daemon-bundle-staleness.test.ts": 347, + "src/main/daemon/daemon-checkpoint-session-queue.test.ts": 204, + "src/main/daemon/daemon-client-notify-settlement.test.ts": 10, + "src/main/daemon/daemon-client-rpc-request.test.ts": 20, + "src/main/daemon/daemon-durable-history-ownership-seed.test.ts": 25, + "src/main/daemon/daemon-endpoint-ownership.test.ts": 26, + "src/main/daemon/daemon-endpoint-publish.test.ts": 92, + "src/main/daemon/daemon-endpoint-windows.test.ts": 8, + "src/main/daemon/daemon-entry-path-layouts.test.ts": 319, + "src/main/daemon/daemon-entry.test.ts": 15, + "src/main/daemon/daemon-errors.test.ts": 7, + "src/main/daemon/daemon-file-log.test.ts": 25, + "src/main/daemon/daemon-foreground-confirmation-protocol.test.ts": 7, + "src/main/daemon/daemon-health-endpoint-entry.test.ts": 9, + "src/main/daemon/daemon-health-socket-cleanup.test.ts": 16, + "src/main/daemon/daemon-health.test.ts": 3195, + "src/main/daemon/daemon-host-relocation.test.ts": 140, + "src/main/daemon/daemon-idle-shutdown.test.ts": 204, + "src/main/daemon/daemon-incarnation-evidence-main-thread.test.ts": 13, + "src/main/daemon/daemon-incarnation-evidence.test.ts": 23, + "src/main/daemon/daemon-init-child-readiness.test.ts": 406, + "src/main/daemon/daemon-init-child-startup-failure.test.ts": 406, + "src/main/daemon/daemon-init-endpoint-adoption.test.ts": 868, + "src/main/daemon/daemon-init-live-session-preservation.test.ts": 541, + "src/main/daemon/daemon-init-packaged-bundle-staleness.test.ts": 353, + "src/main/daemon/daemon-init-provider-installation.test.ts": 398, + "src/main/daemon/daemon-init-replacement-reporting.test.ts": 247, + "src/main/daemon/daemon-init-restart-sequence.test.ts": 576, + "src/main/daemon/daemon-init-wedged-daemon-grace.test.ts": 284, + "src/main/daemon/daemon-lifecycle-event.test.ts": 8, + "src/main/daemon/daemon-main.test.ts": 43, + "src/main/daemon/daemon-native-pty-exception.test.ts": 12, + "src/main/daemon/daemon-pid-file-parse.test.ts": 9, + "src/main/daemon/daemon-preflight-client-replacement.test.ts": 234, + "src/main/daemon/daemon-process-inspection.test.ts": 22, + "src/main/daemon/daemon-protocol-version.test.ts": 9, + "src/main/daemon/daemon-pty-adapter-cold-restore-reanchor.test.ts": 489, + "src/main/daemon/daemon-pty-adapter-cold-restore-seed.test.ts": 297, + "src/main/daemon/daemon-pty-adapter-concurrent-recovery.test.ts": 142, + "src/main/daemon/daemon-pty-adapter-daemon-recovery.test.ts": 505, + "src/main/daemon/daemon-pty-adapter-history-checkpoints.test.ts": 775, + "src/main/daemon/daemon-pty-adapter-history-recovery.test.ts": 580, + "src/main/daemon/daemon-pty-adapter-inventory-respawn.test.ts": 87, + "src/main/daemon/daemon-pty-adapter-protocol-compatibility.test.ts": 73, + "src/main/daemon/daemon-pty-adapter-replacement-exit-race.test.ts": 53, + "src/main/daemon/daemon-pty-adapter-session-adoption.test.ts": 557, + "src/main/daemon/daemon-pty-adapter-steady-state-compat.test.ts": 7, + "src/main/daemon/daemon-pty-adapter.test.ts": 873, + "src/main/daemon/daemon-pty-router-history-handoff.test.ts": 575, + "src/main/daemon/daemon-pty-router.test.ts": 445, + "src/main/daemon/daemon-pty-startup-delivery.test.ts": 1989, + "src/main/daemon/daemon-pty-upgrade-adoption.test.ts": 120, + "src/main/daemon/daemon-pty-write-settlement-recovery.test.ts": 11, + "src/main/daemon/daemon-ready-identity.test.ts": 8, + "src/main/daemon/daemon-reattach-checkpoint-isolation.test.ts": 10265, + "src/main/daemon/daemon-respawn-throttle.test.ts": 9, + "src/main/daemon/daemon-restore-scrollback-depth.test.ts": 2078, + "src/main/daemon/daemon-self-retirement-respawn.test.ts": 148, + "src/main/daemon/daemon-server-async-spawn-cancellation.test.ts": 354, + "src/main/daemon/daemon-server-attach-only.test.ts": 59, + "src/main/daemon/daemon-server-attachment-lifecycle.test.ts": 214, + "src/main/daemon/daemon-server-error-handling.test.ts": 179, + "src/main/daemon/daemon-server-kill-attribution.test.ts": 20, + "src/main/daemon/daemon-server.test.ts": 702, + "src/main/daemon/daemon-session-owner-resolution.test.ts": 22, + "src/main/daemon/daemon-session-scrollback-window.test.ts": 54, + "src/main/daemon/daemon-shutdown-final-checkpoint-caller-deadline.test.ts": 1702, + "src/main/daemon/daemon-spawner.test.ts": 74, + "src/main/daemon/daemon-stream-data-batcher.test.ts": 108, + "src/main/daemon/daemon-stream-droppability-lifecycle.test.ts": 46, + "src/main/daemon/daemon-stream-droppable-membership.test.ts": 40, + "src/main/daemon/daemon-tcc-attribution-main-thread.test.ts": 14, + "src/main/daemon/daemon-tcc-attribution.test.ts": 8, + "src/main/daemon/daemon-transport-attachment-release.test.ts": 114, + "src/main/daemon/degraded-daemon-pty-provider.test.ts": 58, + "src/main/daemon/hangul-cell-width-agreement.test.ts": 37, + "src/main/daemon/headless-emulator-fidelity.fuzz.test.ts": 17026, + "src/main/daemon/headless-emulator-restored-osc-links.test.ts": 50, + "src/main/daemon/headless-emulator-unicode-width.test.ts": 15, + "src/main/daemon/headless-emulator-wide-char-repaint.test.ts": 1773, + "src/main/daemon/headless-emulator-wide-char-snapshot.test.ts": 186, + "src/main/daemon/headless-emulator.test.ts": 114, + "src/main/daemon/headless-osc-link-ranges.test.ts": 119, + "src/main/daemon/hibernation-cold-restore-repro.test.ts": 73, + "src/main/daemon/history-manager-disabled-sessions-leak.test.ts": 58, + "src/main/daemon/history-manager.test.ts": 326, + "src/main/daemon/history-reader-memory.test.ts": 332, + "src/main/daemon/history-reader.test.ts": 85, + "src/main/daemon/issue-6814-daemon-failure-classification.test.ts": 6043, + "src/main/daemon/macos-login-session-death-watch.test.ts": 13, + "src/main/daemon/ndjson.test.ts": 173, + "src/main/daemon/node-pty-error-hints.test.ts": 6, + "src/main/daemon/osc7-file-uri.test.ts": 8, + "src/main/daemon/osc7-uri-extraction.test.ts": 8, + "src/main/daemon/post-ready-flush-gate.test.ts": 16, + "src/main/daemon/pty-session-id.test.ts": 16, + "src/main/daemon/pty-subprocess-cwd-cancel-identity.test.ts": 12, + "src/main/daemon/pty-subprocess-env-inheritance.test.ts": 58, + "src/main/daemon/pty-subprocess-foreground-degraded-scan.test.ts": 64, + "src/main/daemon/pty-subprocess-foreground-identity.test.ts": 303, + "src/main/daemon/pty-subprocess-foreground-scan-cadence.test.ts": 42, + "src/main/daemon/pty-subprocess-git-credential-guard.test.ts": 32, + "src/main/daemon/pty-subprocess-handle-lifecycle.test.ts": 138, + "src/main/daemon/pty-subprocess-io-failure-cleanup.test.ts": 3575, + "src/main/daemon/pty-subprocess-io-failure-native.test.ts": 2227, + "src/main/daemon/pty-subprocess-managed-agent-env.test.ts": 81, + "src/main/daemon/pty-subprocess-windows-shell-launch.test.ts": 66, + "src/main/daemon/pty-subprocess-wsl-launch.test.ts": 52, + "src/main/daemon/pty-subprocess.test.ts": 58, + "src/main/daemon/reattach-snapshot.test.ts": 649, + "src/main/daemon/repro-12101-mouse-tracking-survives-agent-death.test.ts": 61, + "src/main/daemon/repro-7329-remote-snapshot-corruption.test.ts": 57, + "src/main/daemon/session-pending-output.test.ts": 145, + "src/main/daemon/session-shell-recovery.test.ts": 122, + "src/main/daemon/session-terminal-control.test.ts": 46, + "src/main/daemon/session.test.ts": 279, + "src/main/daemon/shell-ready-bash-wrapper.test.ts": 338, + "src/main/daemon/slow-daemon-session-verification.test.ts": 13535, + "src/main/daemon/startup-device-attributes-responder.test.ts": 14, + "src/main/daemon/terminal-checkpoint-serializer.test.ts": 37, + "src/main/daemon/terminal-checkpoint-writer-bounds.test.ts": 91, + "src/main/daemon/terminal-cursor-line-context.test.ts": 26, + "src/main/daemon/terminal-history-incremental-restore.test.ts": 316, + "src/main/daemon/terminal-history-large-checkpoint-cold-restore.test.ts": 9348, + "src/main/daemon/terminal-history-log.test.ts": 13, + "src/main/daemon/terminal-history-permissions.test.ts": 87, + "src/main/daemon/terminal-history-restorable-retention.test.ts": 7, + "src/main/daemon/terminal-history-seed-chunks.test.ts": 7, + "src/main/daemon/terminal-history-seed-segments.test.ts": 22, + "src/main/daemon/terminal-history-seed-transfer-registry.test.ts": 8, + "src/main/daemon/terminal-history-session-tombstone-retry.test.ts": 10, + "src/main/daemon/terminal-host-agent-session.test.ts": 36, + "src/main/daemon/terminal-host-attach-only.test.ts": 28, + "src/main/daemon/terminal-host-cheap-tier-ps-scan-volume.test.ts": 59, + "src/main/daemon/terminal-host-concurrent-create.test.ts": 53, + "src/main/daemon/terminal-host-cwd-readability.test.ts": 30, + "src/main/daemon/terminal-host-non-agent-foreground.test.ts": 17, + "src/main/daemon/terminal-host-process-inspection-cheap-tier.test.ts": 29, + "src/main/daemon/terminal-host-process-inspection.test.ts": 18, + "src/main/daemon/terminal-host-pty-owner-backend.test.ts": 19, + "src/main/daemon/terminal-host-readiness-reporting.test.ts": 23, + "src/main/daemon/terminal-host-session-reaping-leak.test.ts": 30, + "src/main/daemon/terminal-host-startup.test.ts": 45, + "src/main/daemon/terminal-host-teardown-recreate.test.ts": 185, + "src/main/daemon/terminal-host-wsl-context.test.ts": 45, + "src/main/daemon/terminal-host.test.ts": 152, + "src/main/daemon/terminal-session-teardown.test.ts": 16, + "src/main/daemon/terminal-shell-lifecycle-scanner.test.ts": 21, + "src/main/daemon/terminal-shell-recovery-barrier.test.ts": 703, + "src/main/daemon/terminal-shell-recovery-clean-exit-retirement.test.ts": 26, + "src/main/daemon/terminal-snapshot-color-parity.test.ts": 109, + "src/main/daemon/terminal-snapshot-osc8-roundtrip.test.ts": 57, + "src/main/daemon/terminal-snapshot-serialize-roundtrip.test.ts": 130, + "src/main/daemon/windows-conpty-warmup.test.ts": 12, + "src/main/daemon/wsl-cold-restore-cwd.test.ts": 7, + "src/main/destination-serialized-local-rename.test.ts": 330, + "src/main/devin/hook-config-json.test.ts": 11, + "src/main/devin/hook-service.test.ts": 43, + "src/main/diagnostics/main-thread-churn-probe.test.ts": 13, + "src/main/dock/unread-badge.test.ts": 9, + "src/main/droid/hook-service.test.ts": 81, + "src/main/durable-file-write-syscall-proof.test.ts": 125, + "src/main/durable-file-write.test.ts": 89, + "src/main/emulator/android/adb-devices.test.ts": 10, + "src/main/emulator/android/android-app-control.test.ts": 7, + "src/main/emulator/android/android-capability-operations.test.ts": 10, + "src/main/emulator/android/android-device-inventory.test.ts": 12, + "src/main/emulator/android/android-input-commands.test.ts": 5, + "src/main/emulator/android/android-input-mapping.test.ts": 9, + "src/main/emulator/android/android-logcat.test.ts": 5, + "src/main/emulator/android/android-permissions.test.ts": 8, + "src/main/emulator/android/android-sdk-discovery.test.ts": 7, + "src/main/emulator/android/avd-manager.test.ts": 11, + "src/main/emulator/android/scrcpy-server-deploy.test.ts": 7, + "src/main/emulator/android/scrcpy-video-frame-parser.test.ts": 11, + "src/main/emulator/android/uiautomator-tree.test.ts": 12, + "src/main/emulator/backends/android-emulator-backend.test.ts": 24, + "src/main/emulator/backends/ios-emulator-backend.test.ts": 88, + "src/main/emulator/emulator-availability.test.ts": 7, + "src/main/emulator/emulator-bridge.test.ts": 122, + "src/main/emulator/emulator-gesture-sender.test.ts": 131, + "src/main/emulator/emulator-session-registry.test.ts": 5, + "src/main/emulator/emulator-start-lease-registry.test.ts": 62, + "src/main/emulator/mjpeg-frame-parser.test.ts": 8, + "src/main/emulator/mjpeg-frame-stream.test.ts": 34, + "src/main/emulator/scrcpy-video-registry.test.ts": 11, + "src/main/emulator/serve-sim-accessibility-tree.test.ts": 90, + "src/main/emulator/serve-sim-ax-normalization.test.ts": 12, + "src/main/emulator/serve-sim-detached-session.test.ts": 11, + "src/main/emulator/serve-sim-execution.test.ts": 25, + "src/main/emulator/serve-sim-helper-processes.test.ts": 8, + "src/main/emulator/serve-sim-runtime-materializer.test.ts": 49, + "src/main/emulator/simctl-simulator-devices.test.ts": 11, + "src/main/ephemeral-vm-recipe-runner.test.ts": 296, + "src/main/ephemeral-vm-resume-integrity.test.ts": 11, + "src/main/ephemeral-vm-runtime-service.test.ts": 522, + "src/main/ephemeral-vm-runtime-ssh-cleanup.test.ts": 36, + "src/main/external-editor-launch.test.ts": 19, + "src/main/fish-history-session.test.ts": 22, + "src/main/gemini/hook-service.test.ts": 15, + "src/main/ghostty/discovery.test.ts": 14, + "src/main/ghostty/index.test.ts": 20, + "src/main/ghostty/mapper-extended.test.ts": 11, + "src/main/ghostty/mapper.test.ts": 11, + "src/main/ghostty/parser.test.ts": 8, + "src/main/ghostty/theme-import.test.ts": 14, + "src/main/ghostty/theme-resolution.test.ts": 10, + "src/main/git-bash.test.ts": 13, + "src/main/git/add-sparse-worktree.test.ts": 22, + "src/main/git/admission-tier-plumbing.test.ts": 10, + "src/main/git/branch-rename.test.ts": 8, + "src/main/git/canonical-repo-key.test.ts": 6, + "src/main/git/check-ignored-paths.test.ts": 15, + "src/main/git/coalesced-probe.test.ts": 9, + "src/main/git/command-runner/gh-exec-file-deadline.test.ts": 20, + "src/main/git/command-runner/gh-spawn-boundary.test.ts": 566, + "src/main/git/command-runner/git-admission-output-parity.test.ts": 120, + "src/main/git/command-runner/git-admission-span.test.ts": 175, + "src/main/git/command-runner/git-admission-storm-measurement.test.ts": 25859, + "src/main/git/command-runner/git-command-timeout-behavior.test.ts": 1728, + "src/main/git/command-runner/git-command-timeout.test.ts": 8, + "src/main/git/command-runner/git-exec-admission-lifetime.test.ts": 375, + "src/main/git/command-runner/git-spawn-admission-lifetime.test.ts": 15, + "src/main/git/command-runner/git-stream-admission-lifetime.test.ts": 120, + "src/main/git/command-runner/git-subprocess-admission.test.ts": 448, + "src/main/git/command-runner/hosted-cli-deadline-log.test.ts": 22, + "src/main/git/command-runner/wsl-host-failure.test.ts": 17, + "src/main/git/commit-object-ref.test.ts": 8, + "src/main/git/commit.test.ts": 6, + "src/main/git/exact-ref-probe.test.ts": 17, + "src/main/git/fetch-error-classification.test.ts": 9, + "src/main/git/fork-remote-refspec.test.ts": 14, + "src/main/git/fork-remote-stale-branch-refspec.test.ts": 10, + "src/main/git/gh-rate-limit-breaker.test.ts": 20, + "src/main/git/git-capability-state.test.ts": 14, + "src/main/git/git-status-read-lease-owner.test.ts": 15, + "src/main/git/git-upstream-status-read-owner.test.ts": 18, + "src/main/git/hosted-remote-url.test.ts": 6, + "src/main/git/huge-folder-ignore.test.ts": 20, + "src/main/git/local-repo-ref-maintenance.test.ts": 98, + "src/main/git/pack-refs-lock-ownership.test.ts": 36, + "src/main/git/porcelain-v1-records.test.ts": 140, + "src/main/git/remote-ref-probe-cache.test.ts": 13, + "src/main/git/remote-url-probe.test.ts": 9, + "src/main/git/remote.test.ts": 51, + "src/main/git/remove-worktree-branch-cleanup.test.ts": 16, + "src/main/git/remove-worktree-clean-preflight.test.ts": 14, + "src/main/git/remove-worktree.test.ts": 35, + "src/main/git/repo-api-parity.test.ts": 6, + "src/main/git/repo-branch-conflict-batched-probe.test.ts": 11, + "src/main/git/repo-branch-conflict-real-git.test.ts": 157, + "src/main/git/repo-branch-conflict.test.ts": 20, + "src/main/git/repo-clone-path.test.ts": 6, + "src/main/git/repo-default-base-timeout.test.ts": 11, + "src/main/git/repo-default-remote.test.ts": 11, + "src/main/git/repo-detection.test.ts": 348, + "src/main/git/repo-ref-maintenance-real-git.test.ts": 1092, + "src/main/git/repo-remote-drift-real.test.ts": 103, + "src/main/git/repo-remote-drift.test.ts": 8, + "src/main/git/repo-search-ref-compat.test.ts": 10, + "src/main/git/repo-username.test.ts": 30, + "src/main/git/repo.test.ts": 2330, + "src/main/git/runner-command-exec.test.ts": 530, + "src/main/git/runner-gh-host-args.test.ts": 8, + "src/main/git/runner-gh-rate-limit-breaker.test.ts": 32, + "src/main/git/runner-windows-host-environment.test.ts": 88, + "src/main/git/runner-wsl-direct-read.test.ts": 1710, + "src/main/git/runner-wsl-gh-fallback.test.ts": 857, + "src/main/git/runner-wsl-linked-gitdir-timeout.test.ts": 20, + "src/main/git/runner-wsl-login-shell-capture.test.ts": 18, + "src/main/git/runner-wsl-read-routing.test.ts": 15, + "src/main/git/runner.test.ts": 32, + "src/main/git/settled-diff-cache-bounds.test.ts": 7, + "src/main/git/source-control/bulk-pathspec-command-line-budget.test.ts": 128, + "src/main/git/source-control/resolve-git-dir.test.ts": 6, + "src/main/git/source-control/wsl-tracked-pathspec-banner.test.ts": 20, + "src/main/git/status-branch-compare-real-ref.test.ts": 137, + "src/main/git/status-branch-compare.test.ts": 19, + "src/main/git/status-branch-line-total-exec-contract.test.ts": 751, + "src/main/git/status-branch-line-total-real-git.test.ts": 728, + "src/main/git/status-branch-line-total-relay-parity.test.ts": 301, + "src/main/git/status-conflict-operations.test.ts": 19, + "src/main/git/status-conflict-overlap.bench.test.ts": 25, + "src/main/git/status-cquoted-paths.test.ts": 79, + "src/main/git/status-diff-settled-cache.test.ts": 152, + "src/main/git/status-diff.test.ts": 37, + "src/main/git/status-discard-and-bulk-staging.test.ts": 73, + "src/main/git/status-discard-symlink.test.ts": 429, + "src/main/git/status-line-stats-host-paths.test.ts": 14, + "src/main/git/status-pathspec-literals.test.ts": 352, + "src/main/git/status-porcelain-parser.test.ts": 7, + "src/main/git/status-read-coalescing.test.ts": 37, + "src/main/git/status-shared-symlinks.test.ts": 825, + "src/main/git/status-submodule-path-cache.test.ts": 894, + "src/main/git/status-submodule.test.ts": 45, + "src/main/git/status-symlink-probe-budget.test.ts": 44, + "src/main/git/status-upstream-negative-cache.test.ts": 990, + "src/main/git/status-upstream-probe-churn.test.ts": 42, + "src/main/git/status-upstream-ref.test.ts": 13, + "src/main/git/status-wsl-pathspecs.test.ts": 14, + "src/main/git/status.test.ts": 72, + "src/main/git/upstream-deferred-fork-remote-real.test.ts": 49, + "src/main/git/upstream.test.ts": 179, + "src/main/git/worktree-add-creation-config.test.ts": 38, + "src/main/git/worktree-add-local-base-refresh.test.ts": 16, + "src/main/git/worktree-add-local-base-suggestion.test.ts": 15, + "src/main/git/worktree-add-timeout-override.test.ts": 12, + "src/main/git/worktree-base-divergence-real-git.test.ts": 1158, + "src/main/git/worktree-base-divergence.test.ts": 39, + "src/main/git/worktree-base-ref-probe.test.ts": 16, + "src/main/git/worktree-common-dir-comparison.test.ts": 6, + "src/main/git/worktree-configured-paths-concurrency.test.ts": 27, + "src/main/git/worktree-create-preparation-real-git.test.ts": 1004, + "src/main/git/worktree-created-description-real-git.test.ts": 1472, + "src/main/git/worktree-deferred-removal-real-git.test.ts": 269, + "src/main/git/worktree-diff-stamp-guest-gitdir.test.ts": 6, + "src/main/git/worktree-diff-stamp-host-paths.test.ts": 8, + "src/main/git/worktree-git-capabilities.test.ts": 13, + "src/main/git/worktree-graph-listing.test.ts": 13, + "src/main/git/worktree-include-file.test.ts": 83, + "src/main/git/worktree-list-paths.test.ts": 228, + "src/main/git/worktree-list-porcelain.test.ts": 26, + "src/main/git/worktree-listing-created-sparse-distro.test.ts": 9, + "src/main/git/worktree-listing-sparse-distro.test.ts": 8, + "src/main/git/worktree-move.test.ts": 12, + "src/main/git/worktree-mutation-route-invalidation.test.ts": 25, + "src/main/git/worktree-porcelain-parsing.test.ts": 14, + "src/main/git/worktree-preparation-base-oid.test.ts": 14, + "src/main/git/worktree-remove-branch-deletion.test.ts": 17, + "src/main/git/worktree-scan-cache-annotation-reuse.test.ts": 8, + "src/main/git/worktree-scan-cache-sharing.test.ts": 52, + "src/main/git/worktree-separate-git-dir.test.ts": 350, + "src/main/git/worktree-shared-directories.test.ts": 726, + "src/main/git/worktree-sparse-checkout-cache.test.ts": 31, + "src/main/git/worktree-sparse-checkout.test.ts": 276, + "src/main/git/worktree-sparse-state-host-paths.test.ts": 5, + "src/main/git/worktree-symlink-detection.test.ts": 11, + "src/main/git/wsl-direct-git-read-commands.test.ts": 8, + "src/main/git/wsl-linked-worktree-git-route-invalidation.test.ts": 9, + "src/main/git/wsl-linked-worktree-git-routing.test.ts": 57, + "src/main/git/wsl-process-group-termination.test.ts": 8, + "src/main/gitea/client.test.ts": 145, + "src/main/gitea/pull-request-creation.test.ts": 82, + "src/main/gitea/pull-request-mappers.test.ts": 5, + "src/main/gitea/repository-ref.test.ts": 19, + "src/main/github/auth-diagnose.test.ts": 10, + "src/main/github/client-create-pr.test.ts": 39, + "src/main/github/client-file-viewed.test.ts": 10, + "src/main/github/client-issue-origin-preference.test.ts": 17, + "src/main/github/client-issue-source.test.ts": 28, + "src/main/github/client-merge-queue-auto-merge.test.ts": 70, + "src/main/github/client-merged-pr-visibility.test.ts": 19, + "src/main/github/client-pr-branch-discovery.test.ts": 16, + "src/main/github/client-pr-check-details.test.ts": 36, + "src/main/github/client-pr-checks.test.ts": 42, + "src/main/github/client-pr-comment-reactions.test.ts": 13, + "src/main/github/client-pr-conflict-summary.test.ts": 26, + "src/main/github/client-pr-fallback-number.test.ts": 23, + "src/main/github/client-pr-linked-lookup.test.ts": 18, + "src/main/github/client-pr-local-runtime.test.ts": 32, + "src/main/github/client-pr-push-target.test.ts": 24, + "src/main/github/client-pr-state.test.ts": 10, + "src/main/github/client-rate-limit-block.test.ts": 12, + "src/main/github/client-ssh-provider-execution-boundary.test.ts": 23, + "src/main/github/client-stack-merge-guard.test.ts": 32, + "src/main/github/client-starred.test.ts": 12, + "src/main/github/client-tracked-upstream-fork-owner.test.ts": 21, + "src/main/github/client-tracked-upstream-snapshot.test.ts": 232, + "src/main/github/client-work-item-check-summary.test.ts": 12, + "src/main/github/client-work-items-query-paging.test.ts": 35, + "src/main/github/client-work-items.test.ts": 18, + "src/main/github/comment-reactions.test.ts": 3, + "src/main/github/conflict-summary.test.ts": 33, + "src/main/github/default-branch-stale-pr.test.ts": 11, + "src/main/github/gh-utils-concurrency.test.ts": 6, + "src/main/github/gh-utils.test.ts": 88, + "src/main/github/github-api-repository.test.ts": 113, + "src/main/github/github-enterprise-repository.test.ts": 36, + "src/main/github/github-pr-stack.test.ts": 46, + "src/main/github/github-remote-identity-parsing.test.ts": 9, + "src/main/github/github-repository-identity.fork-owner-repo.test.ts": 13, + "src/main/github/github-repository-identity.signed-cache.test.ts": 9, + "src/main/github/github-repository-identity.ssh-host-alias.test.ts": 33, + "src/main/github/issues.test.ts": 18, + "src/main/github/pr-head-tracking-ref.test.ts": 13, + "src/main/github/pr-refresh-coordinator-active-burst-pacing.test.ts": 271, + "src/main/github/pr-refresh-coordinator-active-visible-priority.test.ts": 192, + "src/main/github/pr-refresh-coordinator-alias-coalescing.test.ts": 175, + "src/main/github/pr-refresh-coordinator-rate-limit-budget.test.ts": 223, + "src/main/github/pr-refresh-coordinator-refresh-events.test.ts": 185, + "src/main/github/pr-refresh-coordinator-visible-follow-up.test.ts": 163, + "src/main/github/pr-refresh-error-classification.test.ts": 8, + "src/main/github/pr-refresh-queue-growth-bound.test.ts": 439, + "src/main/github/pr-refresh-validation-backoff.test.ts": 14, + "src/main/github/pr-review-comment-lines.test.ts": 6, + "src/main/github/pr-start-point-compare-base.test.ts": 15, + "src/main/github/pr-start-point.test.ts": 20, + "src/main/github/project-view-host-auth.test.ts": 16, + "src/main/github/project-view.test.ts": 22, + "src/main/github/project-view/mutations.test.ts": 14, + "src/main/github/project-view/project-field-mutations.test.ts": 6, + "src/main/github/project-view/project-view-table.test.ts": 11, + "src/main/github/project-view/repository-field-options.test.ts": 5, + "src/main/github/rate-limit.test.ts": 19, + "src/main/github/review-head-remote.test.ts": 11, + "src/main/github/stacked-pr-creation.test.ts": 23, + "src/main/github/work-item-details-api-parity.test.ts": 7, + "src/main/github/work-item-details-concurrency.test.ts": 7, + "src/main/github/work-item-details-enterprise-host.test.ts": 20, + "src/main/github/work-item-details-file-viewed.test.ts": 18, + "src/main/github/work-item-details-pr-files.test.ts": 22, + "src/main/github/work-item-details.test.ts": 29, + "src/main/gitlab/client-mr-auth-rate-limit.test.ts": 14, + "src/main/gitlab/client-mr-branch-lookup.test.ts": 27, + "src/main/gitlab/client-mr-job-ci.test.ts": 12, + "src/main/gitlab/client-mr-listing.test.ts": 15, + "src/main/gitlab/client-mr-review-actions.test.ts": 11, + "src/main/gitlab/client-work-items.test.ts": 16, + "src/main/gitlab/client.test.ts": 19, + "src/main/gitlab/gitlab-known-host-probe-wsl-fallback.test.ts": 15, + "src/main/gitlab/gitlab-known-host-probe.test.ts": 23, + "src/main/gitlab/gl-utils.test.ts": 41, + "src/main/gitlab/issues.test.ts": 23, + "src/main/gitlab/mappers-workitem.test.ts": 5, + "src/main/gitlab/mappers.test.ts": 23, + "src/main/gitlab/merge-request-creation.test.ts": 11, + "src/main/gitlab/mr-head-tracking-ref.test.ts": 10, + "src/main/gitlab/project-ref-parser.test.ts": 12, + "src/main/gitlab/work-item-details.test.ts": 31, + "src/main/global-fetch-call-site-audit.test.ts": 135, + "src/main/grok-accounts/status.test.ts": 6, + "src/main/grok/grok-hook-config-file.test.ts": 99, + "src/main/grok/grok-hook-owners.test.ts": 26, + "src/main/grok/grok-hook-remnant-removal.test.ts": 15, + "src/main/grok/hook-service.test.ts": 38, + "src/main/grok/windows-grok-hook-script.test.ts": 9, + "src/main/grok/windows-hook-launcher-chain.test.ts": 9, + "src/main/hang-watchdog/hang-detection-marker.test.ts": 7, + "src/main/hang-watchdog/hang-watchdog-detection-loop.test.ts": 11, + "src/main/hang-watchdog/hang-watchdog-worker-path.test.ts": 8, + "src/main/hang-watchdog/main-thread-hang-telemetry.test.ts": 6, + "src/main/hang-watchdog/main-thread-hang-watchdog-entry.test.ts": 19, + "src/main/hang-watchdog/main-thread-hang-watchdog.test.ts": 26, + "src/main/headless-automation-dispatcher-source-boundary.test.ts": 4, + "src/main/hermes/hook-service.test.ts": 196, + "src/main/hooks-effective-hook-resolution.test.ts": 173, + "src/main/hooks-issue-command.test.ts": 237, + "src/main/hooks-orca-yaml-parsing.test.ts": 37, + "src/main/hooks-runner.test.ts": 103, + "src/main/hooks-setup-runner-script.test.ts": 171, + "src/main/hooks.test.ts": 316, + "src/main/host-tree-removal-asar.electron.test.ts": 285, + "src/main/host/deferred-secret-protection-report.test.ts": 16, + "src/main/host/electron-secret-store.test.ts": 11, + "src/main/host/secret-protection-report.test.ts": 13, + "src/main/i18n/main-i18n-lazy-locale.test.ts": 270, + "src/main/ipc/agent-hooks.test.ts": 472, + "src/main/ipc/agent-pane-authority-ownership.test.ts": 4, + "src/main/ipc/ai-vault-scan-coalescing.test.ts": 322, + "src/main/ipc/ai-vault.test.ts": 159, + "src/main/ipc/app.test.ts": 24, + "src/main/ipc/automations-external-scope.test.ts": 110, + "src/main/ipc/bounded-warning-dedupe.test.ts": 8, + "src/main/ipc/browser-client-page-metadata-ipc.test.ts": 19, + "src/main/ipc/browser-preview-tool-authorization.test.ts": 81, + "src/main/ipc/browser-session-profile-ipc.test.ts": 10, + "src/main/ipc/browser-tab-registration-wait.test.ts": 12, + "src/main/ipc/browser.test.ts": 83, + "src/main/ipc/cli-appimage-stale-registration.test.ts": 214, + "src/main/ipc/cli.test.ts": 62, + "src/main/ipc/codex-config-sync.test.ts": 13, + "src/main/ipc/command-path-resolver.test.ts": 13, + "src/main/ipc/computer-use-permissions.test.ts": 9, + "src/main/ipc/crash-reporting-renderer-breadcrumbs.test.ts": 39, + "src/main/ipc/crash-reporting-renderer-error-report-attribution.test.ts": 12, + "src/main/ipc/crash-reporting-replay-guard-wedge-burst.test.ts": 24, + "src/main/ipc/crash-reporting.test.ts": 43, + "src/main/ipc/created-worktree-reconciliation.test.ts": 72, + "src/main/ipc/created-worktree-root-prune.test.ts": 19, + "src/main/ipc/dashboard-payload-validation.test.ts": 74, + "src/main/ipc/dashboard-popout.test.ts": 13, + "src/main/ipc/deferred-emoji-shortcode-dataset.test.ts": 65, + "src/main/ipc/developer-permissions.test.ts": 17, + "src/main/ipc/diagnostics.test.ts": 20, + "src/main/ipc/doc-preview-grant-ipc.test.ts": 10, + "src/main/ipc/dropped-path-resolution.test.ts": 19, + "src/main/ipc/emulator-stream-listener-cleanup.test.ts": 12, + "src/main/ipc/ephemeral-vm-provision-cancel.test.ts": 58, + "src/main/ipc/ephemeral-vm-provisioned-root-ref.test.ts": 447, + "src/main/ipc/ephemeral-vm-runtime-handler-cleanup.test.ts": 94, + "src/main/ipc/ephemeral-vm.test.ts": 595, + "src/main/ipc/feedback-image-attachments.test.ts": 67, + "src/main/ipc/feedback.test.ts": 132, + "src/main/ipc/filesystem-allowed-roots.test.ts": 334, + "src/main/ipc/filesystem-auth.test.ts": 678, + "src/main/ipc/filesystem-branch-compare-diff.test.ts": 24, + "src/main/ipc/filesystem-commit-message-generation.test.ts": 32, + "src/main/ipc/filesystem-commit-message-model-discovery.test.ts": 25, + "src/main/ipc/filesystem-conflict-operation-routing.test.ts": 14, + "src/main/ipc/filesystem-download-transfers.test.ts": 106, + "src/main/ipc/filesystem-git-commit-dispatch.test.ts": 18, + "src/main/ipc/filesystem-git-status-staging.test.ts": 82, + "src/main/ipc/filesystem-import-ssh-ops.test.ts": 23, + "src/main/ipc/filesystem-import-ssh-path-safety.test.ts": 18, + "src/main/ipc/filesystem-import-ssh.test.ts": 31, + "src/main/ipc/filesystem-import.test.ts": 64, + "src/main/ipc/filesystem-list-files-git-directory-expansion.test.ts": 325, + "src/main/ipc/filesystem-list-files-git-fallback-real.test.ts": 271, + "src/main/ipc/filesystem-list-files-install-rg.test.ts": 23, + "src/main/ipc/filesystem-list-files.test.ts": 253, + "src/main/ipc/filesystem-markdown-document-listing.test.ts": 17, + "src/main/ipc/filesystem-mutations.test.ts": 49, + "src/main/ipc/filesystem-path-containment-remote-enoent.test.ts": 8, + "src/main/ipc/filesystem-pull-request-field-generation.test.ts": 18, + "src/main/ipc/filesystem-search-file-paths.test.ts": 257, + "src/main/ipc/filesystem-search-git.test.ts": 150, + "src/main/ipc/filesystem-search-rg-timeout.test.ts": 50, + "src/main/ipc/filesystem-watcher-canonical-root-paths.test.ts": 320, + "src/main/ipc/filesystem-watcher-dormant-rearm.test.ts": 170, + "src/main/ipc/filesystem-watcher-event-batch.test.ts": 9, + "src/main/ipc/filesystem-watcher-ignore.test.ts": 7, + "src/main/ipc/filesystem-watcher-large-batch.test.ts": 150, + "src/main/ipc/filesystem-watcher-local-events.test.ts": 240, + "src/main/ipc/filesystem-watcher-local-unsubscribe.test.ts": 818, + "src/main/ipc/filesystem-watcher-native-capacity.test.ts": 67, + "src/main/ipc/filesystem-watcher-real.test.ts": 488, + "src/main/ipc/filesystem-watcher-remote-batch.test.ts": 27, + "src/main/ipc/filesystem-watcher-remote-cancellation.test.ts": 324, + "src/main/ipc/filesystem-watcher-remote-capacity.test.ts": 31, + "src/main/ipc/filesystem-watcher-remote-rearm.test.ts": 20, + "src/main/ipc/filesystem-watcher-removal-deadline.test.ts": 129, + "src/main/ipc/filesystem-watcher-terminal-resync.test.ts": 21, + "src/main/ipc/filesystem-watcher-unwatchable-roots.test.ts": 24, + "src/main/ipc/filesystem-watcher-wsl.test.ts": 35, + "src/main/ipc/filesystem-watcher.test.ts": 1789, + "src/main/ipc/filesystem.test.ts": 70, + "src/main/ipc/floating-workspace-directory.test.ts": 23, + "src/main/ipc/folder-repo-git-upgrade.test.ts": 2257, + "src/main/ipc/git-status-upstream-ref-watch-request.test.ts": 13, + "src/main/ipc/github-ipc-channel-parity.test.ts": 13, + "src/main/ipc/github-issue-source-preference.test.ts": 16, + "src/main/ipc/github-pr-refresh-routing.test.ts": 31, + "src/main/ipc/github-repo-access-guards.test.ts": 24, + "src/main/ipc/github-ssh-connection-routing.test.ts": 10, + "src/main/ipc/github-star-telemetry.test.ts": 17, + "src/main/ipc/github-work-item-args.test.ts": 12, + "src/main/ipc/github-wsl-runtime-routing.test.ts": 21, + "src/main/ipc/gitlab-repo-access.test.ts": 6, + "src/main/ipc/gitlab.test.ts": 27, + "src/main/ipc/hosted-review.test.ts": 23, + "src/main/ipc/jira-cancellable-requests.test.ts": 12, + "src/main/ipc/keybindings.test.ts": 8, + "src/main/ipc/linear.test.ts": 9, + "src/main/ipc/local-log-tail.test.ts": 9, + "src/main/ipc/local-network-connection-test.test.ts": 84, + "src/main/ipc/local-worktree-runtime-options.test.ts": 10, + "src/main/ipc/macos-keyboard-layout-change-notifications.test.ts": 13, + "src/main/ipc/macos-keyboard-layout-snapshot.test.ts": 7, + "src/main/ipc/markdown-documents.test.ts": 5, + "src/main/ipc/minimax-credentials.test.ts": 17, + "src/main/ipc/mobile.test.ts": 66, + "src/main/ipc/native-chat-subscribe-lifecycle.test.ts": 18, + "src/main/ipc/native-chat.test.ts": 1703, + "src/main/ipc/notebook.test.ts": 13, + "src/main/ipc/notifications-custom-sound.test.ts": 13, + "src/main/ipc/notifications-delivery-gating.test.ts": 19, + "src/main/ipc/notifications-message-formatting.test.ts": 32, + "src/main/ipc/notifications-mobile-fanout.test.ts": 17, + "src/main/ipc/notifications-permission-onboarding.test.ts": 27, + "src/main/ipc/notifications-retention-lifecycle.test.ts": 27, + "src/main/ipc/orca-profile-auth-handlers.test.ts": 16, + "src/main/ipc/orca-profile-org-members-handlers.test.ts": 10, + "src/main/ipc/orca-profiles.test.ts": 28, + "src/main/ipc/parcel-watcher-child-launch.test.ts": 14, + "src/main/ipc/parcel-watcher-child-registry.test.ts": 60, + "src/main/ipc/parcel-watcher-crash-fuse.test.ts": 4, + "src/main/ipc/parcel-watcher-disconnect-termination.test.ts": 46, + "src/main/ipc/parcel-watcher-entry-path.test.ts": 8, + "src/main/ipc/parcel-watcher-event-cancellation.test.ts": 12, + "src/main/ipc/parcel-watcher-event-delivery.test.ts": 8, + "src/main/ipc/parcel-watcher-process-entry.test.ts": 83, + "src/main/ipc/parcel-watcher-process.test.ts": 395, + "src/main/ipc/parcel-watcher-root-path-rewrite.test.ts": 19, + "src/main/ipc/parcel-watcher-shallow-subscription.test.ts": 11, + "src/main/ipc/parcel-watcher-supervisor-capacity-wait.test.ts": 340, + "src/main/ipc/parcel-watcher-supervisor-capacity.test.ts": 134, + "src/main/ipc/parcel-watcher-unsubscribe-timeout.test.ts": 38, + "src/main/ipc/pet-bundle.test.ts": 6, + "src/main/ipc/pet.test.ts": 35, + "src/main/ipc/plugin-marketplaces.test.ts": 13, + "src/main/ipc/plugins.test.ts": 18, + "src/main/ipc/preflight-agent-detection-no-subprocess.test.ts": 22, + "src/main/ipc/preflight-agent-detection.test.ts": 42, + "src/main/ipc/preflight-agent-refresh.test.ts": 21, + "src/main/ipc/preflight-command-exec.test.ts": 8, + "src/main/ipc/preflight-host-cli-status.test.ts": 27, + "src/main/ipc/preflight-remote-ssh.test.ts": 16, + "src/main/ipc/preflight-wsl-agent-detection.test.ts": 55, + "src/main/ipc/preflight-wsl-command.test.ts": 17, + "src/main/ipc/pty-activation-inventory-scope.test.ts": 23, + "src/main/ipc/pty-agent-session-write-gate.test.ts": 141, + "src/main/ipc/pty-applied-size-reporting.test.ts": 48, + "src/main/ipc/pty-buffer-snapshot-dispatch.test.ts": 64, + "src/main/ipc/pty-codex-account-attribution.test.ts": 46, + "src/main/ipc/pty-controller-owner-recovery.test.ts": 53, + "src/main/ipc/pty-controller-ownership-routing.test.ts": 37, + "src/main/ipc/pty-controller-process-inventory.test.ts": 17, + "src/main/ipc/pty-controller-spawn-admission.test.ts": 49, + "src/main/ipc/pty-cumulative-ack-accounting.test.ts": 95, + "src/main/ipc/pty-daemon-controller-teardown.test.ts": 44, + "src/main/ipc/pty-daemon-spawn-agent-home-env.test.ts": 72, + "src/main/ipc/pty-daemon-spawn-codex-auth.test.ts": 82, + "src/main/ipc/pty-daemon-spawn-session-identity.test.ts": 60, + "src/main/ipc/pty-daemon-spawn-wsl-runtime.test.ts": 67, + "src/main/ipc/pty-daemon-ssh-lease-lifecycle.test.ts": 48, + "src/main/ipc/pty-dead-owner-respawn.test.ts": 44, + "src/main/ipc/pty-delivery-health-heal.test.ts": 117, + "src/main/ipc/pty-dispatcher-handshake-osc-answers.test.ts": 83, + "src/main/ipc/pty-encoding.test.ts": 7, + "src/main/ipc/pty-global-renderer-credit.test.ts": 134, + "src/main/ipc/pty-hidden-at-spawn-mark.test.ts": 120, + "src/main/ipc/pty-hidden-delivery-gate.test.ts": 8, + "src/main/ipc/pty-ipc-hidden-delivery-gate.test.ts": 67, + "src/main/ipc/pty-ipc-producer-flow-control.test.ts": 57, + "src/main/ipc/pty-listener-teardown-and-orphans.test.ts": 99, + "src/main/ipc/pty-login-shell-startup-commands.test.ts": 113, + "src/main/ipc/pty-management.test.ts": 46, + "src/main/ipc/pty-output-batching-drain.test.ts": 66, + "src/main/ipc/pty-output-drain-rounds.test.ts": 110, + "src/main/ipc/pty-pane-claim-arbitration.test.ts": 137, + "src/main/ipc/pty-pane-materialization-race.test.ts": 501, + "src/main/ipc/pty-pane-reservation-settlement.test.ts": 71, + "src/main/ipc/pty-pending-data-drain-queue-differential.test.ts": 955, + "src/main/ipc/pty-pending-data-drain-queue.test.ts": 19, + "src/main/ipc/pty-pending-data-drain-scheduler-differential.test.ts": 19, + "src/main/ipc/pty-pending-output-cap.test.ts": 97, + "src/main/ipc/pty-pending-projection-admissions.test.ts": 86, + "src/main/ipc/pty-persisted-incarnation-repair.test.ts": 38, + "src/main/ipc/pty-producer-flow-control.test.ts": 16, + "src/main/ipc/pty-renderer-inflight-credit.test.ts": 156, + "src/main/ipc/pty-renderer-lifecycle-delivery-reset.test.ts": 77, + "src/main/ipc/pty-renderer-liveness-guard.test.ts": 82, + "src/main/ipc/pty-renderer-send-failure-recovery.test.ts": 51, + "src/main/ipc/pty-restore-record-seeding.test.ts": 156, + "src/main/ipc/pty-restored-appimage-cli-shim-refresh.test.ts": 14, + "src/main/ipc/pty-runtime-kill-and-exit.test.ts": 105, + "src/main/ipc/pty-runtime-ssh-binding-persistence.test.ts": 40, + "src/main/ipc/pty-serializer-settlement-mapping.test.ts": 146, + "src/main/ipc/pty-session-liveness-and-ownership.test.ts": 42, + "src/main/ipc/pty-spawn-codex-home-unavailable.test.ts": 43, + "src/main/ipc/pty-spawn-cwd-fallback.test.ts": 69, + "src/main/ipc/pty-spawn-env-agent-overlays.test.ts": 116, + "src/main/ipc/pty-spawn-env-codex-home-routing.test.ts": 132, + "src/main/ipc/pty-spawn-env-codex-resume-provenance.test.ts": 118, + "src/main/ipc/pty-spawn-env-terminal-basics.test.ts": 1128, + "src/main/ipc/pty-spawn-runtime-handle-binding.test.ts": 135, + "src/main/ipc/pty-ssh-stop-verdict.test.ts": 92, + "src/main/ipc/pty-ssh-undelivered-kill.test.ts": 149, + "src/main/ipc/pty-startup-barrier-and-listing.test.ts": 60, + "src/main/ipc/pty-startup-barrier-ordering.test.ts": 3, + "src/main/ipc/pty-startup-swap-window-presence.test.ts": 81, + "src/main/ipc/pty-windows-shell-selection.test.ts": 131, + "src/main/ipc/pty-write-ipc-validation.test.ts": 125, + "src/main/ipc/pty-wsl-cwd-validation.test.ts": 75, + "src/main/ipc/pty/delivery/attached-pty-size.test.ts": 13, + "src/main/ipc/pty/ipc/spawn-commit-ssh-lease-cardinality.test.ts": 111, + "src/main/ipc/pty/ipc/spawn-push-target-materialization-real-git.test.ts": 258, + "src/main/ipc/pty/ipc/spawn-push-target-materialization.test.ts": 11, + "src/main/ipc/pty/ipc/spawn-reattach-size-cache.test.ts": 14, + "src/main/ipc/pty/ipc/write-input-chunk-yield.test.ts": 25, + "src/main/ipc/pty/pane/launch-authority.test.ts": 9, + "src/main/ipc/pty/pane/stable-pane-absence-death-certificate.test.ts": 26, + "src/main/ipc/pty/pane/stable-pane-relay-absence-respawn.test.ts": 14, + "src/main/ipc/pty/register-headless-runtime.test.ts": 6, + "src/main/ipc/pty/runtime/queried-host-kinds.test.ts": 8, + "src/main/ipc/pty/runtime/spawn-commit-pty-size.test.ts": 7, + "src/main/ipc/rate-limits.test.ts": 7, + "src/main/ipc/readdir-error-diagnostics.test.ts": 10, + "src/main/ipc/register-core-handlers/register-core-handlers.test.ts": 20, + "src/main/ipc/remote-watcher-event-batch.test.ts": 29, + "src/main/ipc/remote-workspace-cache.test.ts": 9, + "src/main/ipc/remote-workspace-patch-queue.test.ts": 173, + "src/main/ipc/remote-workspace-snapshot-normalization.test.ts": 10, + "src/main/ipc/remote-workspace-stale-resync.test.ts": 162, + "src/main/ipc/remote-workspace.test.ts": 29, + "src/main/ipc/renderer-shutdown-checkpoint.test.ts": 18, + "src/main/ipc/renderer-terminal-serializer-readiness.test.ts": 11, + "src/main/ipc/repos-add-linked-worktree.test.ts": 46, + "src/main/ipc/repos-create.test.ts": 34, + "src/main/ipc/repos-execution-host-catalog.test.ts": 22, + "src/main/ipc/repos-local-add-and-project-setup.test.ts": 54, + "src/main/ipc/repos-local-clone-lifecycle.test.ts": 245, + "src/main/ipc/repos-nested-import.test.ts": 51, + "src/main/ipc/repos-nested-scan.test.ts": 36, + "src/main/ipc/repos-picker.test.ts": 8, + "src/main/ipc/repos-remote-base-ref-queries.test.ts": 30, + "src/main/ipc/repos-remote-client-events.test.ts": 1415, + "src/main/ipc/repos-remote-git-username.test.ts": 6, + "src/main/ipc/repos-remote.test.ts": 66, + "src/main/ipc/repos-sparse-presets.test.ts": 27, + "src/main/ipc/repos/remote-repo-registration.test.ts": 7, + "src/main/ipc/rg-availability.test.ts": 9, + "src/main/ipc/runtime-environment-browser-client-host-handler.test.ts": 14, + "src/main/ipc/runtime-environment-capability-evidence.test.ts": 9, + "src/main/ipc/runtime-environment-diagnostics-broadcast.test.ts": 4, + "src/main/ipc/runtime-environment-federated-read-routing.test.ts": 34, + "src/main/ipc/runtime-environment-federated-read-transport.bench.test.ts": 10, + "src/main/ipc/runtime-environment-removal-storage.test.ts": 565, + "src/main/ipc/runtime-environment-request-connections.test.ts": 1282, + "src/main/ipc/runtime-environment-revision-guard.test.ts": 7, + "src/main/ipc/runtime-environment-status-connection.test.ts": 441, + "src/main/ipc/runtime-environment-status-recovery.test.ts": 23, + "src/main/ipc/runtime-environment-support-routing.test.ts": 62, + "src/main/ipc/runtime-environment-transport-routing-tailscale-hint.test.ts": 853, + "src/main/ipc/runtime-environments-call-routing.test.ts": 187, + "src/main/ipc/runtime-environments-capability-cache.test.ts": 129, + "src/main/ipc/runtime-environments-pairing.test.ts": 66, + "src/main/ipc/runtime-environments-status-diagnostics.test.ts": 37, + "src/main/ipc/runtime-environments-subscription-lifecycle.test.ts": 35, + "src/main/ipc/runtime-environments-subscription-routing.test.ts": 26, + "src/main/ipc/runtime-environments-subscription-teardown.test.ts": 38, + "src/main/ipc/runtime-subscribe-lifecycle.test.ts": 10, + "src/main/ipc/runtime-watcher-pending-assignment.test.ts": 281, + "src/main/ipc/runtime-watcher-process-pool.test.ts": 46, + "src/main/ipc/runtime.test.ts": 23, + "src/main/ipc/settings.test.ts": 30, + "src/main/ipc/shallow-watch-delivery-probe.test.ts": 28, + "src/main/ipc/shell.test.ts": 47, + "src/main/ipc/skill-cloud-install-ipc-schemas.test.ts": 13, + "src/main/ipc/skill-cloud-ipc-handlers.test.ts": 12, + "src/main/ipc/skill-install-management-ipc-handlers.test.ts": 19, + "src/main/ipc/skill-install-progress-ipc.test.ts": 7, + "src/main/ipc/skill-ipc-main-window.test.ts": 9, + "src/main/ipc/skills.test.ts": 17, + "src/main/ipc/source-control-ai-linked-issue.test.ts": 14, + "src/main/ipc/speech.test.ts": 12, + "src/main/ipc/ssh-app-shutdown.test.ts": 45, + "src/main/ipc/ssh-browse.test.ts": 586, + "src/main/ipc/ssh-disconnect-cancellation.test.ts": 1118, + "src/main/ipc/ssh-handler-reregistration.test.ts": 49, + "src/main/ipc/ssh-passphrase.test.ts": 6, + "src/main/ipc/ssh-pty-closed-generation-ranges.test.ts": 46, + "src/main/ipc/ssh-pty-consumer-identity.test.ts": 175, + "src/main/ipc/ssh-pty-legacy-projection.test.ts": 18, + "src/main/ipc/ssh-pty-model-admission-generation-scope.test.ts": 16, + "src/main/ipc/ssh-pty-model-admission.test.ts": 15, + "src/main/ipc/ssh-pty-output-exit-deadline.test.ts": 23, + "src/main/ipc/ssh-pty-output-generation-guard.test.ts": 10, + "src/main/ipc/ssh-pty-output-intake.test.ts": 57, + "src/main/ipc/ssh-pty-output-model-migration.test.ts": 25, + "src/main/ipc/ssh-pty-remote-source-range-consumers.test.ts": 20, + "src/main/ipc/ssh-pty-source-ack-coalescer.test.ts": 14, + "src/main/ipc/ssh-pty-source-ack-session-contract.test.ts": 13, + "src/main/ipc/ssh-pty-source-obligation-coordinator.test.ts": 14, + "src/main/ipc/ssh-pty-source-obligation-ledger.test.ts": 82, + "src/main/ipc/ssh-relay-reset-resume.test.ts": 908, + "src/main/ipc/ssh-state-broadcast-fanout.test.ts": 25, + "src/main/ipc/ssh-target-registry.test.ts": 1633, + "src/main/ipc/ssh-terminate-sessions.test.ts": 144, + "src/main/ipc/ssh.test.ts": 61, + "src/main/ipc/telemetry.test.ts": 13, + "src/main/ipc/terminal-git-credential-guard.test.ts": 13, + "src/main/ipc/terminal-preview-output-stream.test.ts": 9, + "src/main/ipc/terminal-preview.test.ts": 155, + "src/main/ipc/terminal-render-desync-evidence.test.ts": 35, + "src/main/ipc/tui-agent-detection-commands.test.ts": 5, + "src/main/ipc/ui.test.ts": 12, + "src/main/ipc/usage-provider-handlers.test.ts": 6, + "src/main/ipc/watched-worktree-catalog-notification.test.ts": 7, + "src/main/ipc/watcher-event-root-path-rewrite.test.ts": 18, + "src/main/ipc/watcher-removal-gate.test.ts": 12, + "src/main/ipc/workspace-cleanup-activity.test.ts": 33, + "src/main/ipc/workspace-cleanup-broad-scan.test.ts": 55, + "src/main/ipc/workspace-cleanup-execution-host-routing.test.ts": 28, + "src/main/ipc/workspace-cleanup-local-git-routing.test.ts": 9, + "src/main/ipc/workspace-cleanup-snapshot-ipc.test.ts": 33, + "src/main/ipc/workspace-cleanup.test.ts": 4276, + "src/main/ipc/workspace-create-error-classifier.test.ts": 10, + "src/main/ipc/workspace-ports.test.ts": 21, + "src/main/ipc/workspace-space.test.ts": 18, + "src/main/ipc/worktree-base-directory-change-collector.test.ts": 8, + "src/main/ipc/worktree-base-directory-event-filter.test.ts": 18, + "src/main/ipc/worktree-base-directory-poller-marker-fanout.test.ts": 142, + "src/main/ipc/worktree-base-directory-poller.test.ts": 2628, + "src/main/ipc/worktree-base-directory-watch-targets.test.ts": 319, + "src/main/ipc/worktree-base-directory-watcher.test.ts": 109, + "src/main/ipc/worktree-create-lineage.test.ts": 16, + "src/main/ipc/worktree-folder-rename-target.test.ts": 8, + "src/main/ipc/worktree-git-common-polling.test.ts": 1088, + "src/main/ipc/worktree-git-common-watch.test.ts": 2677, + "src/main/ipc/worktree-git-status-ref-watch.test.ts": 10, + "src/main/ipc/worktree-head-identity-reader-concurrency.test.ts": 29, + "src/main/ipc/worktree-head-identity-reader-incremental.test.ts": 104, + "src/main/ipc/worktree-head-identity-reader.test.ts": 38, + "src/main/ipc/worktree-head-identity-refresh.test.ts": 28, + "src/main/ipc/worktree-include-copy-budget.test.ts": 53, + "src/main/ipc/worktree-logic-created-agent.test.ts": 5, + "src/main/ipc/worktree-logic-wsl.test.ts": 21, + "src/main/ipc/worktree-logic.test.ts": 53, + "src/main/ipc/worktree-metadata-merge.test.ts": 5, + "src/main/ipc/worktree-path-deduplication.test.ts": 34, + "src/main/ipc/worktree-push-target-cleanup.test.ts": 20, + "src/main/ipc/worktree-push-target-reconciliation-real-git.test.ts": 930, + "src/main/ipc/worktree-push-target-reconciliation.test.ts": 19, + "src/main/ipc/worktree-push-target-refspec-migration.test.ts": 11, + "src/main/ipc/worktree-push-target-refspec-real-git.test.ts": 2112, + "src/main/ipc/worktree-push-target-remote-scan.test.ts": 14, + "src/main/ipc/worktree-push-target-setup.test.ts": 16, + "src/main/ipc/worktree-remote-push-target-materialization.test.ts": 31, + "src/main/ipc/worktree-remote-ssh-branch-conflict.test.ts": 11, + "src/main/ipc/worktree-symlink-reconciliation.real.test.ts": 49, + "src/main/ipc/worktree-symlinks.test.ts": 123, + "src/main/ipc/worktree-watcher-removal-binding.test.ts": 78, + "src/main/ipc/worktrees-authoritative-local-metadata-pruning.test.ts": 210, + "src/main/ipc/worktrees-create-execution-host-routing.test.ts": 45, + "src/main/ipc/worktrees-create-metadata-persistence.test.ts": 60, + "src/main/ipc/worktrees-delete-pty-teardown.test.ts": 53, + "src/main/ipc/worktrees-detected-scan-cache.test.ts": 97, + "src/main/ipc/worktrees-discovery-metadata-backfill.test.ts": 51, + "src/main/ipc/worktrees-existing-branch-checkout.test.ts": 71, + "src/main/ipc/worktrees-forget-local.test.ts": 39, + "src/main/ipc/worktrees-issue-command-overrides.test.ts": 25, + "src/main/ipc/worktrees-lineage-hydration.test.ts": 33, + "src/main/ipc/worktrees-listing-fallback-rows.test.ts": 39, + "src/main/ipc/worktrees-local-base-ref-resolution.test.ts": 82, + "src/main/ipc/worktrees-local-create-flow.test.ts": 76, + "src/main/ipc/worktrees-orphan-directory-cleanup.test.ts": 35, + "src/main/ipc/worktrees-preserved-branch-fork-remote.test.ts": 28, + "src/main/ipc/worktrees-removal-recovery.test.ts": 75, + "src/main/ipc/worktrees-remove-archive-hooks.test.ts": 35, + "src/main/ipc/worktrees-remove-host-disambiguation.test.ts": 32, + "src/main/ipc/worktrees-remove-preflight.test.ts": 33, + "src/main/ipc/worktrees-setup-launch-sparse-checkout.test.ts": 57, + "src/main/ipc/worktrees-ssh-base-ref-resolution.test.ts": 61, + "src/main/ipc/worktrees-ssh-branch-conflict-suffixing.test.ts": 51, + "src/main/ipc/worktrees-ssh-create-base-prefetch.test.ts": 124, + "src/main/ipc/worktrees-ssh-fork-push-target-remote.test.ts": 73, + "src/main/ipc/worktrees-ssh-local-base-refresh.test.ts": 86, + "src/main/ipc/worktrees-ssh-pr-head-fetch.test.ts": 43, + "src/main/ipc/worktrees-ssh-provider-authority.test.ts": 35, + "src/main/ipc/worktrees-ssh-repo-owner-resolution.test.ts": 42, + "src/main/ipc/worktrees-ssh-setup-launch.test.ts": 48, + "src/main/ipc/worktrees-windows.test.ts": 72, + "src/main/ipc/worktrees-wsl-runtime-routing.test.ts": 84, + "src/main/ipc/worktrees/listing/detected-provider-listing-meta-index.test.ts": 19, + "src/main/ipc/worktrees/listing/detected-scan-failure-authority.test.ts": 29, + "src/main/ipc/worktrees/listing/detected-worktree-classification.test.ts": 21, + "src/main/ipc/worktrees/listing/detected-worktree-scan-hygiene-gate.test.ts": 13, + "src/main/ipc/worktrees/listing/register-sparse-checkout-cache-invalidation.test.ts": 9, + "src/main/jira/adf-markdown.test.ts": 10, + "src/main/jira/attachment-image-cache-generation.test.ts": 11, + "src/main/jira/attachment-image-cache.test.ts": 9, + "src/main/jira/attachment-images.test.ts": 31, + "src/main/jira/client.test.ts": 302, + "src/main/jira/issues.test.ts": 62, + "src/main/jira/jira-issue-mutations.test.ts": 29, + "src/main/jira/jira-issue-summary-timeout.test.ts": 33, + "src/main/jira/jira-search-abort.test.ts": 24, + "src/main/keybindings/keybinding-file.test.ts": 53, + "src/main/keybindings/keybinding-service.test.ts": 39, + "src/main/kimi/hook-service.test.ts": 8, + "src/main/kimi/kimi-hook-config-toml.test.ts": 25, + "src/main/kimi/kimi-runtime-home.test.ts": 15, + "src/main/lib/unread-response-body.test.ts": 51, + "src/main/line-editor-ready-output-scanner.test.ts": 7, + "src/main/linear/client.test.ts": 97, + "src/main/linear/issue-context-client.test.ts": 23, + "src/main/linear/issue-context-current.test.ts": 6, + "src/main/linear/issue-context-errors.test.ts": 6, + "src/main/linear/issue-context-includes.test.ts": 27, + "src/main/linear/issue-context-inline-media.test.ts": 6, + "src/main/linear/issue-context-relations.test.ts": 10, + "src/main/linear/issue-context.test.ts": 16, + "src/main/linear/issue-list-filter.test.ts": 8, + "src/main/linear/issue-relation-write.test.ts": 19, + "src/main/linear/issue-search-summary.test.ts": 6, + "src/main/linear/issues.test.ts": 41, + "src/main/linear/mappers.test.ts": 8, + "src/main/linear/mcp-issue-list-pagination.test.ts": 56, + "src/main/linear/mcp-issue-list.test.ts": 93, + "src/main/linear/projects.test.ts": 306, + "src/main/linear/teams.test.ts": 20, + "src/main/linux-lid-sleep-assertion.test.ts": 14, + "src/main/linux-package-install-command.test.ts": 36, + "src/main/linux-package-install-diagnostic.test.ts": 17, + "src/main/linux-package-update-recovery.test.ts": 178, + "src/main/linux-update-package-type.test.ts": 86, + "src/main/local-builds/local-build-candidate.test.ts": 107, + "src/main/local-builds/local-build-compatibility-contract.test.ts": 6, + "src/main/local-builds/local-build-feed-server.test.ts": 68, + "src/main/local-downloaded-folder-promotion.test.ts": 101, + "src/main/local-worktree-filesystem.test.ts": 269, + "src/main/local-worktree-metadata-prune-gate.test.ts": 9, + "src/main/local-worktree-path-presence.test.ts": 18, + "src/main/local-worktree-removal-recovery.test.ts": 22, + "src/main/localhost-worktree-label-proxy.test.ts": 72, + "src/main/macos-full-disk-access-status.test.ts": 8, + "src/main/macos-press-and-hold-default.test.ts": 13, + "src/main/macos-system-sleep-assertion.test.ts": 20, + "src/main/macos-tcc-prompt-notice.test.ts": 16, + "src/main/macos-tcc-prompt-watch.test.ts": 22, + "src/main/main-process-tree-kill-gate.test.ts": 7, + "src/main/memory/collector-windows-sweep.test.ts": 198, + "src/main/memory/collector.test.ts": 239, + "src/main/memory/host-memory.test.ts": 21, + "src/main/memory/hydrate-local-pty-registry.test.ts": 873, + "src/main/memory/process-memory-metric.test.ts": 6, + "src/main/memory/windows-process-sample-parsing.test.ts": 86, + "src/main/menu/gpu-acceleration-about-panel.test.ts": 9, + "src/main/menu/register-app-menu.test.ts": 21, + "src/main/mimo/hook-service.test.ts": 11, + "src/main/minimax/minimax-api-key-store.test.ts": 30, + "src/main/minimax/minimax-cookie-store.test.ts": 38, + "src/main/native-chat/agent-session-journal/journal-corruption-repair.test.ts": 293, + "src/main/native-chat/agent-session-journal/journal-crash-boundary.test.ts": 97, + "src/main/native-chat/agent-session-journal/journal-cursor.test.ts": 9, + "src/main/native-chat/agent-session-journal/journal-database.test.ts": 95, + "src/main/native-chat/agent-session-journal/journal-epoch-replacement.test.ts": 38, + "src/main/native-chat/agent-session-journal/journal-file-format-remnant.test.ts": 94, + "src/main/native-chat/agent-session-journal/journal-handle-ownership.test.ts": 7709, + "src/main/native-chat/agent-session-journal/journal-item-identity.test.ts": 10, + "src/main/native-chat/agent-session-journal/journal-legacy-import.test.ts": 181, + "src/main/native-chat/agent-session-journal/journal-reducer.test.ts": 45, + "src/main/native-chat/agent-session-journal/journal-row-schema-version.test.ts": 28, + "src/main/native-chat/agent-session-journal/journal-row-schema.test.ts": 32, + "src/main/native-chat/agent-session-journal/journal-row-writer.test.ts": 28, + "src/main/native-chat/agent-session-journal/journal-store-close.test.ts": 85, + "src/main/native-chat/agent-session-journal/journal-store-schema.test.ts": 85, + "src/main/native-chat/agent-session-journal/journal-store.test.ts": 290, + "src/main/native-chat/agent-session-journal/journal-subagent-liveness.test.ts": 835, + "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer-protected.test.ts": 46, + "src/main/native-chat/agent-session-wire/agent-session-delta-coalescer.test.ts": 20, + "src/main/native-chat/agent-session-wire/agent-session-history-forward-read-budget.test.ts": 477, + "src/main/native-chat/agent-session-wire/agent-session-history-page-grouping-parity.test.ts": 253, + "src/main/native-chat/agent-session-wire/agent-session-history-page-scaling.test.ts": 17, + "src/main/native-chat/agent-session-wire/agent-session-history-page.test.ts": 1870, + "src/main/native-chat/agent-session-wire/agent-session-journal-recovery.test.ts": 178, + "src/main/native-chat/agent-session-wire/provider-frame-activity.test.ts": 10, + "src/main/native-chat/agent-session-wire/provider-frame-disposition.test.ts": 15, + "src/main/native-chat/agent-session-wire/provider-turn-activity-routing.test.ts": 22, + "src/main/native-chat/agent-session-wire/structured-agent-session-acquisition-options.test.ts": 564, + "src/main/native-chat/agent-session-wire/structured-agent-session-adapter-router.test.ts": 15, + "src/main/native-chat/agent-session-wire/structured-agent-session-adapter.test.ts": 14, + "src/main/native-chat/agent-session-wire/structured-agent-session-adopted-import.test.ts": 165, + "src/main/native-chat/agent-session-wire/structured-agent-session-claude-options-round-trip.test.ts": 248, + "src/main/native-chat/agent-session-wire/structured-agent-session-close-retry.test.ts": 52, + "src/main/native-chat/agent-session-wire/structured-agent-session-command-publication.test.ts": 45, + "src/main/native-chat/agent-session-wire/structured-agent-session-event-sink.test.ts": 24, + "src/main/native-chat/agent-session-wire/structured-agent-session-eviction.test.ts": 16, + "src/main/native-chat/agent-session-wire/structured-agent-session-forget-status.test.ts": 30, + "src/main/native-chat/agent-session-wire/structured-agent-session-grouped-prompt.test.ts": 68, + "src/main/native-chat/agent-session-wire/structured-agent-session-handoff-flow-runner.test.ts": 99, + "src/main/native-chat/agent-session-wire/structured-agent-session-handoff-operation-guard.test.ts": 144, + "src/main/native-chat/agent-session-wire/structured-agent-session-handoff-options.test.ts": 587, + "src/main/native-chat/agent-session-wire/structured-agent-session-handoff-owner-close.test.ts": 5, + "src/main/native-chat/agent-session-wire/structured-agent-session-handoff-reverse.test.ts": 6, + "src/main/native-chat/agent-session-wire/structured-agent-session-handoff.test.ts": 207, + "src/main/native-chat/agent-session-wire/structured-agent-session-holds.test.ts": 215, + "src/main/native-chat/agent-session-wire/structured-agent-session-host-handoff.test.ts": 103, + "src/main/native-chat/agent-session-wire/structured-agent-session-host-runtime-state.test.ts": 7, + "src/main/native-chat/agent-session-wire/structured-agent-session-host.test.ts": 1194, + "src/main/native-chat/agent-session-wire/structured-agent-session-journal-handles.test.ts": 66, + "src/main/native-chat/agent-session-wire/structured-agent-session-late-settlement.test.ts": 832, + "src/main/native-chat/agent-session-wire/structured-agent-session-launch-env.test.ts": 7, + "src/main/native-chat/agent-session-wire/structured-agent-session-lease-renewer.test.ts": 156, + "src/main/native-chat/agent-session-wire/structured-agent-session-live-tui-restart-survival.test.ts": 431, + "src/main/native-chat/agent-session-wire/structured-agent-session-option-settlement.test.ts": 189, + "src/main/native-chat/agent-session-wire/structured-agent-session-processless-reservation.test.ts": 115, + "src/main/native-chat/agent-session-wire/structured-agent-session-proven-dead-retry.test.ts": 130, + "src/main/native-chat/agent-session-wire/structured-agent-session-provider-restore.test.ts": 288, + "src/main/native-chat/agent-session-wire/structured-agent-session-read-restore.test.ts": 290, + "src/main/native-chat/agent-session-wire/structured-agent-session-readable-restorer.test.ts": 7, + "src/main/native-chat/agent-session-wire/structured-agent-session-recovery-exits.test.ts": 361, + "src/main/native-chat/agent-session-wire/structured-agent-session-recovery-resolution.test.ts": 180, + "src/main/native-chat/agent-session-wire/structured-agent-session-refusal-retry.test.ts": 8912, + "src/main/native-chat/agent-session-wire/structured-agent-session-restart-reconcile.test.ts": 6, + "src/main/native-chat/agent-session-wire/structured-agent-session-restart-restore-gate.test.ts": 6, + "src/main/native-chat/agent-session-wire/structured-agent-session-restart-restore.test.ts": 59, + "src/main/native-chat/agent-session-wire/structured-agent-session-restart-status-publication.test.ts": 137, + "src/main/native-chat/agent-session-wire/structured-agent-session-reveal.test.ts": 19, + "src/main/native-chat/agent-session-wire/structured-agent-session-rewind.test.ts": 1652, + "src/main/native-chat/agent-session-wire/structured-agent-session-send-idempotency.test.ts": 34, + "src/main/native-chat/agent-session-wire/structured-agent-session-send-settlement.test.ts": 20, + "src/main/native-chat/agent-session-wire/structured-agent-session-send.test.ts": 515, + "src/main/native-chat/agent-session-wire/structured-agent-session-settled-attach-retry.test.ts": 304, + "src/main/native-chat/agent-session-wire/structured-agent-session-stale-turn-verdict.test.ts": 13, + "src/main/native-chat/agent-session-wire/structured-agent-session-status-feed.test.ts": 581, + "src/main/native-chat/agent-session-wire/structured-agent-session-subscribers.test.ts": 314, + "src/main/native-chat/agent-session-wire/structured-agent-session-surface-lifetime.test.ts": 1169, + "src/main/native-chat/agent-session-wire/structured-agent-session-task-queue.test.ts": 64, + "src/main/native-chat/agent-session-wire/structured-agent-session-teardown-handoff-drain.test.ts": 251, + "src/main/native-chat/agent-session-wire/structured-agent-session-turns.test.ts": 47, + "src/main/native-chat/agent-session-wire/structured-agent-session-unexpected-exit.test.ts": 15, + "src/main/native-chat/agent-session-wire/structured-agent-session-wedged-profile-migration.test.ts": 985, + "src/main/native-chat/agent-session-wire/structured-agent-session-wire-admission.test.ts": 569, + "src/main/native-chat/agent-session-wire/structured-conversation-command-admission.test.ts": 6, + "src/main/native-chat/agent-session-wire/structured-conversation-command.test.ts": 831, + "src/main/native-chat/agent-session-wire/structured-conversation-replacements.test.ts": 8, + "src/main/native-chat/agent-session-wire/structured-rewind-claude-proof.test.ts": 9, + "src/main/native-chat/agent-session-wire/structured-rewind-journal-body.test.ts": 16, + "src/main/native-chat/agent-session-wire/structured-session-compaction.test.ts": 21, + "src/main/native-chat/agent-session-wire/structured-tui-transcript-catchup.test.ts": 420, + "src/main/native-chat/agent-session-wire/unhandled-provider-frame.test.ts": 21, + "src/main/native-chat/claude-structured-managed-account-support.test.ts": 11, + "src/main/native-chat/host-readable-transcript-path-fs-gate.test.ts": 213, + "src/main/native-chat/host-readable-transcript-path.test.ts": 20, + "src/main/native-chat/native-chat-file-provenance.test.ts": 10, + "src/main/native-chat/session-file-resolver-claude-roots.test.ts": 6, + "src/main/native-chat/session-file-resolver-codex-roots.test.ts": 4, + "src/main/native-chat/session-file-resolver-wsl-scan-gate.test.ts": 21, + "src/main/native-chat/session-file-resolver-wsl.test.ts": 21, + "src/main/native-chat/session-file-resolver.test.ts": 87, + "src/main/native-chat/structured-agent-session-create-support.test.ts": 8, + "src/main/native-chat/structured-agent-session-history-adoption.test.ts": 9, + "src/main/native-chat/subagent-entry-id-bounds.test.ts": 7, + "src/main/native-chat/transcript-fallback-id.test.ts": 6, + "src/main/native-chat/transcript-interruption-message.test.ts": 7, + "src/main/native-chat/transcript-line-decoders-claude-control-bytes.test.ts": 7, + "src/main/native-chat/transcript-line-decoders-claude-image-companion.test.ts": 6, + "src/main/native-chat/transcript-line-decoders-codex-skill-context.test.ts": 12, + "src/main/native-chat/transcript-line-decoders.grok.test.ts": 13, + "src/main/native-chat/transcript-line-decoders.omp.test.ts": 21, + "src/main/native-chat/transcript-read-cache-refusal-recovery.test.ts": 31, + "src/main/native-chat/transcript-read-cache-wsl-stall.test.ts": 14, + "src/main/native-chat/transcript-read-cache.test.ts": 55, + "src/main/native-chat/transcript-reader-codex-history-mode.test.ts": 21, + "src/main/native-chat/transcript-reader-wsl-stall.test.ts": 20, + "src/main/native-chat/transcript-reader.test.ts": 31, + "src/main/native-chat/transcript-stream-lines.test.ts": 8, + "src/main/native-chat/transcript-tail-reader-cancellation.test.ts": 115, + "src/main/native-chat/transcript-tail-reader-wsl-gate.test.ts": 17, + "src/main/native-chat/transcript-turn-lifecycle.test.ts": 11, + "src/main/native-chat/transcript-watch-engine-wsl-lifecycle.test.ts": 71, + "src/main/native-chat/transcript-watch-error.test.ts": 2068, + "src/main/native-chat/transcript-watch-liveness.test.ts": 639, + "src/main/native-chat/transcript-watch-resolve-poll.test.ts": 82, + "src/main/native-chat/transcript-watch-unflushed-settle.test.ts": 94, + "src/main/native-chat/transcript-watch-unsubscribe-race.test.ts": 26, + "src/main/native-chat/transcript-watch-wsl-exact-path.test.ts": 21, + "src/main/native-chat/transcript-watch-wsl-stall.test.ts": 21, + "src/main/native-chat/transcript-watch.test.ts": 3183, + "src/main/native-chat/transcript-window-tool-attribution.test.ts": 13, + "src/main/native-chat/wsl-codex-session-path-scan.test.ts": 24, + "src/main/native-chat/wsl-transcript-fs-access.test.ts": 29, + "src/main/native-chat/wsl-transcript-fs-gate.test.ts": 463, + "src/main/native-chat/wsl-transcript-fs-process-client.test.ts": 33, + "src/main/native-chat/wsl-transcript-fs-process-operations.test.ts": 5, + "src/main/native-chat/wsl-transcript-fs-route-quarantine.test.ts": 22, + "src/main/native-chat/wsl-transcript-fs-route.test.ts": 6, + "src/main/native-chat/wsl-transcript-running-observer.test.ts": 65, + "src/main/network/electron-proxy-request-guard.test.ts": 314, + "src/main/network/macos-system-resolver-health.test.ts": 18, + "src/main/network/macos-tailscale-dns-diagnostic.test.ts": 8, + "src/main/network/proxy-settings-session.test.ts": 779, + "src/main/network/proxy-settings.test.ts": 67, + "src/main/notifications/desktop-away-state.test.ts": 6, + "src/main/observability/architecture.test.ts": 10, + "src/main/observability/bundle.test.ts": 294, + "src/main/observability/diagnostic-upload-http.test.ts": 8, + "src/main/observability/instrumentation.test.ts": 252, + "src/main/observability/local-file-sink-memory.test.ts": 126, + "src/main/observability/local-file-sink.test.ts": 27, + "src/main/observability/redactor-environment-lines.test.ts": 486, + "src/main/observability/redactor.test.ts": 27, + "src/main/observability/tracer.test.ts": 27, + "src/main/opencode-usage/scanner-windows-data-directory.test.ts": 23, + "src/main/opencode-usage/scanner-wsl-gate.test.ts": 18, + "src/main/opencode-usage/scanner.test.ts": 224, + "src/main/opencode-usage/store.test.ts": 28, + "src/main/opencode/hook-plugin-background-child-completion.test.ts": 197, + "src/main/opencode/hook-plugin-child-attention.test.ts": 421, + "src/main/opencode/hook-plugin-fail-open-ownership.test.ts": 460, + "src/main/opencode/hook-plugin-lifecycle-delivery.test.ts": 591, + "src/main/opencode/hook-plugin-message-part-throttle.test.ts": 137, + "src/main/opencode/hook-plugin-module-contract.test.ts": 146, + "src/main/opencode/hook-service.test.ts": 50, + "src/main/opencode/opencode-data-directory.test.ts": 6, + "src/main/orca-profiles/profile-artifact-cloud-cleanup.test.ts": 142, + "src/main/orca-profiles/profile-cloud-auth-config.test.ts": 7, + "src/main/orca-profiles/profile-cloud-auth-status.test.ts": 7, + "src/main/orca-profiles/profile-cloud-client.test.ts": 73, + "src/main/orca-profiles/profile-cloud-dev-service.test.ts": 52, + "src/main/orca-profiles/profile-cloud-org-members-client.test.ts": 9, + "src/main/orca-profiles/profile-cloud-org-members-service.test.ts": 33, + "src/main/orca-profiles/profile-cloud-pkce.test.ts": 184, + "src/main/orca-profiles/profile-cloud-service-auth-retry.test.ts": 42, + "src/main/orca-profiles/profile-cloud-service-refresh.test.ts": 148, + "src/main/orca-profiles/profile-cloud-service.test.ts": 45, + "src/main/orca-profiles/profile-cloud-session-mutation.test.ts": 14, + "src/main/orca-profiles/profile-cloud-session-refresh.test.ts": 12, + "src/main/orca-profiles/profile-cloud-session-store.test.ts": 71, + "src/main/orca-profiles/profile-index-store.test.ts": 188, + "src/main/orca-profiles/profile-project-presence.test.ts": 59, + "src/main/orca-profiles/profile-project-session-field-disposition.test.ts": 9, + "src/main/orca-profiles/profile-project-session-state.test.ts": 13, + "src/main/orca-profiles/profile-project-state-file.test.ts": 7, + "src/main/orca-profiles/profile-project-transfer.test.ts": 238, + "src/main/orca-profiles/profile-ui-scope.test.ts": 4, + "src/main/orcad/electron-serve-browser-process.test.ts": 2342, + "src/main/orcad/electron-serve-provider-selection.test.ts": 427, + "src/main/orcad/electron-sidecar-method-routing.test.ts": 7, + "src/main/orcad/electron-sidecar-tab-registry.test.ts": 12, + "src/main/orcad/external-chromium-browser-session.test.ts": 16, + "src/main/orcad/main-preflight-order.test.ts": 8, + "src/main/orcad/native-host-abi.test.ts": 15, + "src/main/orcad/node-pty-prebuilt-slot.test.ts": 12, + "src/main/orcad/node-pty-precondition.test.ts": 290, + "src/main/orcad/orcad-app-paths.test.ts": 10, + "src/main/orcad/orcad-bind-address.test.ts": 11, + "src/main/orcad/orcad-browser-provider.test.ts": 53, + "src/main/orcad/orcad-bundle-native-load-order.test.ts": 3283, + "src/main/orcad/orcad-daemon-supervision.test.ts": 10, + "src/main/orcad/orcad-forked-child-paths.test.ts": 8, + "src/main/orcad/orcad-health.test.ts": 10, + "src/main/orcad/orcad-instance-lock.test.ts": 15, + "src/main/orcad/orcad-launch-contract.test.ts": 13, + "src/main/orcad/orcad-native-preflight.test.ts": 6, + "src/main/orcad/orcad-push-startup.test.ts": 704, + "src/main/own-chromium-tree-kill-guard.test.ts": 20, + "src/main/persistence-async-write-syscalls.test.ts": 5455, + "src/main/persistence-automations.test.ts": 593, + "src/main/persistence-clipboard-selection-migration.test.ts": 65, + "src/main/persistence-cohort-and-identity-migration.test.ts": 67, + "src/main/persistence-cross-host-pane-identity.test.ts": 36, + "src/main/persistence-deregistered-repo-residue.test.ts": 132, + "src/main/persistence-duplicate-repo-id-host-scope.test.ts": 792, + "src/main/persistence-feature-interaction-broadcast.benchmark.test.ts": 126, + "src/main/persistence-floating-terminal-trust.test.ts": 91, + "src/main/persistence-flush-and-save-scheduling.test.ts": 226, + "src/main/persistence-folder-workspace-notes.test.ts": 124, + "src/main/persistence-host-admitted-terminal-membership.test.ts": 32, + "src/main/persistence-host-partitioned-sessions.test.ts": 173, + "src/main/persistence-host-partitioned-ssh-pty-bindings.test.ts": 31, + "src/main/persistence-initial-load.test.ts": 171, + "src/main/persistence-layout-binding-recovery.test.ts": 46, + "src/main/persistence-load-repair-durability.test.ts": 65, + "src/main/persistence-loading-store-extraction.test.ts": 93, + "src/main/persistence-loading-store-write-risks.test.ts": 34, + "src/main/persistence-native-chat-tab-view-mode.test.ts": 17, + "src/main/persistence-pane-identity-migration.test.ts": 27776, + "src/main/persistence-protected-secret-fail-closed.test.ts": 2755, + "src/main/persistence-protected-secret-write-race.test.ts": 698, + "src/main/persistence-proxy-secret-recovery.test.ts": 1089, + "src/main/persistence-pty-binding-leaf-tab-resolution.test.ts": 33, + "src/main/persistence-pty-binding-reconciliation.test.ts": 54, + "src/main/persistence-remote-session-startup.test.ts": 58, + "src/main/persistence-repo-lifecycle.test.ts": 807, + "src/main/persistence-right-sidebar-tab.test.ts": 7, + "src/main/persistence-settings-ui-defaults.test.ts": 137, + "src/main/persistence-settings-update.test.ts": 124, + "src/main/persistence-single-serialize.test.ts": 1727, + "src/main/persistence-source-control-ai-migration.test.ts": 108, + "src/main/persistence-split-pane-incarnation.test.ts": 65, + "src/main/persistence-ssh-lease-reattach-reclaim.test.ts": 46, + "src/main/persistence-ssh-lease-tombstone-retention.test.ts": 44, + "src/main/persistence-ssh-pending-pty-kill.test.ts": 570, + "src/main/persistence-ssh-readoption-automation-migration.test.ts": 1520, + "src/main/persistence-ssh-remote-pty-binding-replay.test.ts": 41, + "src/main/persistence-ssh-remote-pty-leases.test.ts": 129, + "src/main/persistence-ssh-targets-and-pane-keys.test.ts": 72, + "src/main/persistence-terminal-option-key-migration.test.ts": 47, + "src/main/persistence-ui-state.test.ts": 3109, + "src/main/persistence-update-repo.test.ts": 252, + "src/main/persistence-workspace-pinned-automation-fence.test.ts": 1660, + "src/main/persistence-workspace-repin-automation-owner.test.ts": 1100, + "src/main/persistence-workspace-session-scrollback.test.ts": 101, + "src/main/persistence-workspace-status-workflow.test.ts": 67, + "src/main/persistence-worktree-card-properties.test.ts": 43, + "src/main/persistence-worktree-deletion-fencing.test.ts": 88, + "src/main/persistence-worktree-lineage-and-backups.test.ts": 499, + "src/main/persistence-worktree-meta-and-folder-workspaces.test.ts": 83, + "src/main/persistence-worktree-name-retirement.test.ts": 3387, + "src/main/persistence-worktree-visibility.test.ts": 100, + "src/main/persistence/applying-settings/settings-update-terminal-contrast.test.ts": 8, + "src/main/persistence/applying-settings/terminal-settings-migrations.test.ts": 4, + "src/main/persistence/leasing-ssh-ptys/ssh-pty-binding-cleanup.test.ts": 22, + "src/main/persistence/loading-store/metadata-lineage-batch-pruning.test.ts": 46, + "src/main/persistence/loading-store/normalize-loaded-global-settings.test.ts": 17, + "src/main/persistence/loading-store/normalize-loaded-project-catalog.test.ts": 12, + "src/main/persistence/loading-store/persisted-state-redundancy.test.ts": 461, + "src/main/persistence/loading-store/secret-sentinel-substitution.test.ts": 14, + "src/main/persistence/loading-store/state-write-round-trip.test.ts": 72, + "src/main/persistence/loading-store/store-prune-gate-signals.test.ts": 32, + "src/main/persistence/loading-store/store-runtime-authored-session-writes.test.ts": 29, + "src/main/persistence/loading-store/workspace-session-partitions.test.ts": 16, + "src/main/persistence/loading-store/workspace-session-terminal-binding-replay.test.ts": 13, + "src/main/persistence/loading-store/worktree-meta-alias-projection.test.ts": 345, + "src/main/persistence/loading-store/worktree-meta-write-normalization.test.ts": 8, + "src/main/persistence/restoring-sessions/pane-alias-normalization-scaling.test.ts": 11, + "src/main/persistence/restoring-sessions/pane-alias-normalization.test.ts": 6, + "src/main/persistence/restoring-sessions/pane-key-remapping.test.ts": 9, + "src/main/persistence/restoring-sessions/session-owner-fields.test.ts": 10, + "src/main/persistence/restoring-sessions/session-worktree-ownership.test.ts": 21, + "src/main/persistence/restoring-sessions/terminal-layout-normalization.test.ts": 6, + "src/main/persistence/restoring-sessions/workspace-pane-normalization-index.test.ts": 10, + "src/main/persistence/runtime-authored-workspace-session-fields.test.ts": 5, + "src/main/persistence/scheduling-automations/automation-run-operations.test.ts": 16, + "src/main/persistence/tracking-repos/local-worktree-metadata-scan-expectation.test.ts": 21, + "src/main/persistence/tracking-repos/missing-local-worktree-metadata-pruning.test.ts": 107, + "src/main/persistence/tracking-repos/probeable-local-worktree-metadata-candidates.test.ts": 13, + "src/main/persistence/tracking-repos/project-host-compatibility.test.ts": 11, + "src/main/persistence/tracking-repos/worktree-metadata-normalization.test.ts": 9, + "src/main/pi/agent-status-extension-omp-lifecycle.test.ts": 839, + "src/main/pi/agent-status-extension-source.test.ts": 1483, + "src/main/pi/agent-status-omp-approval-forwarding.test.ts": 487, + "src/main/pi/agent-status-owner-recovery.test.ts": 432, + "src/main/pi/agent-status-ui-prompt.test.ts": 623, + "src/main/pi/titlebar-extension-overlay-path.test.ts": 9, + "src/main/pi/titlebar-extension-service.test.ts": 147, + "src/main/pi/titlebar-extension-source.test.ts": 618, + "src/main/plugins/plugin-atomic-file-write.test.ts": 1988, + "src/main/plugins/plugin-audit-log-scaling.test.ts": 412, + "src/main/plugins/plugin-audit-log.test.ts": 17, + "src/main/plugins/plugin-bundled-bootstrap-coordinator.test.ts": 62, + "src/main/plugins/plugin-bundled-bootstrap.test.ts": 104, + "src/main/plugins/plugin-command-registry.test.ts": 32, + "src/main/plugins/plugin-content-pack-registry.test.ts": 30, + "src/main/plugins/plugin-content-safety.test.ts": 66, + "src/main/plugins/plugin-dev-watcher.test.ts": 106, + "src/main/plugins/plugin-discovery.test.ts": 40, + "src/main/plugins/plugin-enablement.test.ts": 14, + "src/main/plugins/plugin-host-conformance.test.ts": 30, + "src/main/plugins/plugin-host-methods.test.ts": 22, + "src/main/plugins/plugin-host-process.test.ts": 13, + "src/main/plugins/plugin-host-runtime.test.ts": 11, + "src/main/plugins/plugin-install-trust.test.ts": 51, + "src/main/plugins/plugin-install.test.ts": 334, + "src/main/plugins/plugin-kill-list-content-revocation.test.ts": 52, + "src/main/plugins/plugin-kill-list-service.test.ts": 68, + "src/main/plugins/plugin-language-pack-registry.test.ts": 22, + "src/main/plugins/plugin-launch-content.test.ts": 84, + "src/main/plugins/plugin-list-projection.test.ts": 15, + "src/main/plugins/plugin-marketplace-installer.test.ts": 98, + "src/main/plugins/plugin-marketplace-service.test.ts": 136, + "src/main/plugins/plugin-marketplace-store.test.ts": 241, + "src/main/plugins/plugin-panel-controller.test.ts": 25, + "src/main/plugins/plugin-panel-navigation-guard.test.ts": 7, + "src/main/plugins/plugin-panel-owner-lifecycle.test.ts": 4, + "src/main/plugins/plugin-panel-sessions.test.ts": 7, + "src/main/plugins/plugin-private-marketplace.integration.test.ts": 321, + "src/main/plugins/plugin-secrets-store.test.ts": 33, + "src/main/plugins/plugin-service-integrity.test.ts": 70, + "src/main/plugins/plugin-service-reconciliation.test.ts": 314, + "src/main/plugins/plugin-startup-budget.test.ts": 373, + "src/main/plugins/plugin-storage-store.test.ts": 8, + "src/main/plugins/plugin-vm-recipe-registry.test.ts": 40, + "src/main/plugins/plugin-worker-controller.test.ts": 77, + "src/main/plugins/plugin-worker-env.test.ts": 6, + "src/main/plugins/plugin-worker-manager.test.ts": 17, + "src/main/plugins/plugin-worker-output-buffer.test.ts": 12, + "src/main/plugins/plugin-worker-supervision.integration.test.ts": 8191, + "src/main/ports/advertised-url-watcher-pid-validation.test.ts": 10, + "src/main/ports/advertised-url-watcher.test.ts": 22, + "src/main/ports/local-workspace-port-scanner.test.ts": 44, + "src/main/ports/port-scan-command-client.test.ts": 1741, + "src/main/ports/port-scan-command-execution.test.ts": 5223, + "src/main/ports/port-scan-command-import-boundary.test.ts": 6, + "src/main/ports/ssh-advertised-url-enrichment.test.ts": 13, + "src/main/ports/workspace-port-ownership.test.ts": 9, + "src/main/powershell-osc133-bootstrap.test.ts": 14, + "src/main/project-groups/folder-workspace-path-status.test.ts": 19, + "src/main/project-groups/nested-repo-discovery.test.ts": 103, + "src/main/project-groups/nested-repo-import-target.test.ts": 10, + "src/main/project-groups/nested-repo-import.test.ts": 17, + "src/main/project-runtime-git-options.test.ts": 15, + "src/main/protected-secret-persistence.test.ts": 20, + "src/main/providers/agent-foreground-process-batch.test.ts": 15, + "src/main/providers/agent-foreground-process-pi.test.ts": 12, + "src/main/providers/agent-foreground-process-ps-scan-volume.test.ts": 24, + "src/main/providers/agent-foreground-process-real-rows.test.ts": 13, + "src/main/providers/agent-foreground-process-remote-evidence.test.ts": 11, + "src/main/providers/agent-foreground-process-windows-job-rejection.test.ts": 4, + "src/main/providers/agent-foreground-process.test.ts": 30, + "src/main/providers/execution-host-provider-dispatch.test.ts": 12, + "src/main/providers/local-pty-foreground-inspection-cheap-tier.test.ts": 10, + "src/main/providers/local-pty-provider-foreground-process.test.ts": 110, + "src/main/providers/local-pty-provider-io-events.test.ts": 31, + "src/main/providers/local-pty-provider-session-inventory.test.ts": 29, + "src/main/providers/local-pty-provider-shell-readiness.test.ts": 39, + "src/main/providers/local-pty-provider-shutdown.test.ts": 351, + "src/main/providers/local-pty-provider-spawn-cwd-safety.test.ts": 21, + "src/main/providers/local-pty-provider-spawn-env.test.ts": 40, + "src/main/providers/local-pty-provider-spawn-session.test.ts": 33, + "src/main/providers/local-pty-provider-windows-shell-launch.test.ts": 72, + "src/main/providers/local-pty-shell-ready-marker-scan.test.ts": 8, + "src/main/providers/local-pty-shell-ready-startup-command.test.ts": 11, + "src/main/providers/local-pty-shell-ready-wrapper-generation.test.ts": 411, + "src/main/providers/local-pty-shell-startup-command.node-pty.test.ts": 52, + "src/main/providers/local-pty-utils-windows-fallback.test.ts": 9, + "src/main/providers/local-pty-utils.test.ts": 12, + "src/main/providers/macos-login-session-pty-probe.test.ts": 12, + "src/main/providers/macos-tcc-login-shell.test.ts": 77, + "src/main/providers/posix-pane-foreground-fingerprint.test.ts": 9, + "src/main/providers/process-cwd.test.ts": 100, + "src/main/providers/provider-dispatch.test.ts": 38, + "src/main/providers/pty-default-cwd.test.ts": 9, + "src/main/providers/pty-process-inspection.test.ts": 15, + "src/main/providers/pty-process-list-admission.test.ts": 76, + "src/main/providers/pty-repaint-wide-char-buffer.node-pty.test.ts": 1855, + "src/main/providers/settled-pty-writer-census.test.ts": 79, + "src/main/providers/ssh-filesystem-dispatch.test.ts": 9, + "src/main/providers/ssh-filesystem-doc-preview.test.ts": 7, + "src/main/providers/ssh-filesystem-download.test.ts": 145, + "src/main/providers/ssh-filesystem-provider-capabilities.test.ts": 15, + "src/main/providers/ssh-filesystem-provider-download-folder.test.ts": 22, + "src/main/providers/ssh-filesystem-provider-range.test.ts": 13, + "src/main/providers/ssh-filesystem-provider-stream.test.ts": 52, + "src/main/providers/ssh-filesystem-provider-watch-waiters.test.ts": 437, + "src/main/providers/ssh-filesystem-provider.test.ts": 59, + "src/main/providers/ssh-filesystem-watch-notifications.test.ts": 11, + "src/main/providers/ssh-git-dispatch.test.ts": 4, + "src/main/providers/ssh-git-provider-api.test.ts": 8, + "src/main/providers/ssh-git-provider-commit-message.test.ts": 18, + "src/main/providers/ssh-git-provider-diff.test.ts": 32, + "src/main/providers/ssh-git-provider-exec.test.ts": 26, + "src/main/providers/ssh-git-provider-merge.test.ts": 9, + "src/main/providers/ssh-git-provider-remote-sync.test.ts": 21, + "src/main/providers/ssh-git-provider-staging.test.ts": 16, + "src/main/providers/ssh-git-provider-status-lease.test.ts": 24, + "src/main/providers/ssh-git-provider-status.test.ts": 22, + "src/main/providers/ssh-git-provider-upstream-lease.test.ts": 16, + "src/main/providers/ssh-git-provider-worktree.test.ts": 29, + "src/main/providers/ssh-git-worktree-list-dedupe.test.ts": 32, + "src/main/providers/ssh-pty-inspect-observation-identity.test.ts": 6, + "src/main/providers/ssh-pty-live-source-restore-respawn.test.ts": 15, + "src/main/providers/ssh-pty-notification-rejection-routing.test.ts": 162, + "src/main/providers/ssh-pty-notification-routing.test.ts": 22, + "src/main/providers/ssh-pty-provider-agent-session-create-operation.test.ts": 35, + "src/main/providers/ssh-pty-provider-claim-incarnation.test.ts": 7, + "src/main/providers/ssh-pty-provider-exit-race.test.ts": 19, + "src/main/providers/ssh-pty-provider-process-events.test.ts": 18, + "src/main/providers/ssh-pty-provider-reattach-incarnation.test.ts": 9, + "src/main/providers/ssh-pty-provider-spawn.test.ts": 33, + "src/main/providers/ssh-pty-provider-terminal-repair.test.ts": 36, + "src/main/providers/ssh-pty-provider.test.ts": 31, + "src/main/providers/ssh-pty-reattach-absence-discrimination.test.ts": 23, + "src/main/providers/ssh-pty-relay-absence-verdict.test.ts": 19, + "src/main/providers/ssh-pty-source-delivery-ledger.test.ts": 14, + "src/main/providers/ssh-pty-write.test.ts": 25, + "src/main/providers/ssh-worktree-catalog-authority.test.ts": 29, + "src/main/providers/stable-foreground-process.test.ts": 5, + "src/main/providers/windows-agent-foreground-process-scan-volume.test.ts": 37, + "src/main/providers/windows-cached-agent-revalidation.test.ts": 11, + "src/main/providers/windows-conpty-wide-char-duplication.node-pty.test.ts": 7, + "src/main/providers/windows-console-attached-processes.test.ts": 16, + "src/main/providers/windows-foreground-process-inspection-cost.test.ts": 15, + "src/main/providers/windows-foreground-process-rows.test.ts": 13, + "src/main/providers/windows-powershell-executable.test.ts": 18, + "src/main/providers/windows-powershell.test.ts": 8, + "src/main/providers/windows-pty-job-membership.test.ts": 13, + "src/main/providers/windows-shell-args.test.ts": 48, + "src/main/providers/windows-shell-fallback-chain.test.ts": 10, + "src/main/providers/working-directory-validation.test.ts": 45, + "src/main/provisioned-root-ssh-adoption.test.ts": 140, + "src/main/proxy-guarded-fetch-call-site-audit.test.ts": 76, + "src/main/pty-descendant-termination-job-coverage.test.ts": 5, + "src/main/pty-descendant-termination.test.ts": 59, + "src/main/pty/appimage-terminal-env.test.ts": 6, + "src/main/pty/build-mode-env.test.ts": 6, + "src/main/pty/codex-home-wsl-env.test.ts": 5, + "src/main/pty/codex-preflight-profile-path-rewrite.test.ts": 134, + "src/main/pty/codex-shell-launch-preflight.test.ts": 4086, + "src/main/pty/conda-activation-env.test.ts": 8, + "src/main/pty/legacy-terminal-shim-dir.test.ts": 1197, + "src/main/pty/legacy-terminal-windows-tombstone.test.ts": 21, + "src/main/pty/node-pty-master-fd-retirement.test.ts": 5158, + "src/main/pty/node-pty-self-exit-pseudoconsole-close.test.ts": 6, + "src/main/pty/omp-shell-wrapper-alias-safety.test.ts": 16, + "src/main/pty/omp-sqlite-overlay.test.ts": 5, + "src/main/pty/overlay-mirror.test.ts": 17, + "src/main/pty/posix-pty-foreground-group.test.ts": 9, + "src/main/pty/posix-pty-process-groups.integration.test.ts": 106, + "src/main/pty/posix-pty-process-groups.test.ts": 10, + "src/main/pty/shell-startup-env.test.ts": 19, + "src/main/pty/terminal-color-env.test.ts": 8, + "src/main/pty/windows-environment-path-main-loop.test.ts": 367, + "src/main/pty/windows-environment-path.test.ts": 17, + "src/main/pty/windows-path-registry-change.test.ts": 6, + "src/main/pty/windows-path-registry-fallback.test.ts": 7, + "src/main/pty/windows-path-registry-reader.test.ts": 9, + "src/main/pty/wsl-orca-env.test.ts": 20, + "src/main/pwsh.test.ts": 24, + "src/main/quit-path-durable-write-blocking.test.ts": 1453, + "src/main/quit-teardown-agent-browser-daemons.test.ts": 6, + "src/main/quit-teardown-deadline.test.ts": 11, + "src/main/quit-teardown-start-gate.test.ts": 8, + "src/main/rate-limits/account-runtime-target-sync.test.ts": 12, + "src/main/rate-limits/antigravity-usage-mirror.test.ts": 7, + "src/main/rate-limits/auth-filesystem-operation.test.ts": 11, + "src/main/rate-limits/claude-fetcher-cli-fallback.test.ts": 86, + "src/main/rate-limits/claude-fetcher-fable-usage.test.ts": 65, + "src/main/rate-limits/claude-fetcher-keychain-credentials.test.ts": 100, + "src/main/rate-limits/claude-fetcher-managed-account-usage.test.ts": 50, + "src/main/rate-limits/claude-oauth-usage-error.test.ts": 44, + "src/main/rate-limits/claude-pty.test.ts": 64, + "src/main/rate-limits/claude-usage-error-classification.test.ts": 9, + "src/main/rate-limits/claude-usage-refresh-plan.test.ts": 7, + "src/main/rate-limits/codex-auth-presence.test.ts": 17, + "src/main/rate-limits/codex-fetcher-auth-errors.test.ts": 15, + "src/main/rate-limits/codex-fetcher-backend.test.ts": 76, + "src/main/rate-limits/codex-fetcher-probe-shutdown.test.ts": 38, + "src/main/rate-limits/codex-fetcher-process-contract.test.ts": 43, + "src/main/rate-limits/codex-fetcher-pty-settle.test.ts": 38, + "src/main/rate-limits/codex-fetcher-rpc-exit-diagnostics.test.ts": 175, + "src/main/rate-limits/codex-fetcher-runtime-pairing.test.ts": 17, + "src/main/rate-limits/codex-fetcher-session-supplement.test.ts": 20, + "src/main/rate-limits/codex-fetcher.test.ts": 86, + "src/main/rate-limits/codex-probe-termination.test.ts": 16, + "src/main/rate-limits/codex-pty-rate-limit-probe.test.ts": 10, + "src/main/rate-limits/codex-pty-status-parser.test.ts": 7, + "src/main/rate-limits/codex-rate-limit-window-classification.test.ts": 11, + "src/main/rate-limits/codex-rpc-rate-limit-probe.test.ts": 60, + "src/main/rate-limits/gemini-bucket-formatting.test.ts": 6, + "src/main/rate-limits/gemini-usage-fetcher.fallback.test.ts": 10, + "src/main/rate-limits/gemini-usage-fetcher.test.ts": 19, + "src/main/rate-limits/grok-auth.test.ts": 41, + "src/main/rate-limits/grok-fetcher.test.ts": 27, + "src/main/rate-limits/hidden-pty-cleanup.test.ts": 12, + "src/main/rate-limits/initial-account-rate-limit-target.test.ts": 14, + "src/main/rate-limits/kimi-fetcher-wsl-home.test.ts": 12, + "src/main/rate-limits/kimi-fetcher.test.ts": 34, + "src/main/rate-limits/minimax/minimax-fetcher.test.ts": 46, + "src/main/rate-limits/minimax/minimax-request-context.test.ts": 26, + "src/main/rate-limits/opencode-go-usage-fetcher.test.ts": 34, + "src/main/rate-limits/service-account-target-selection.test.ts": 142, + "src/main/rate-limits/service-antigravity-usage.test.ts": 11, + "src/main/rate-limits/service-inactive-account-previews.test.ts": 27, + "src/main/rate-limits/service-live-claude-usage.test.ts": 37, + "src/main/rate-limits/service-minimax-usage.test.ts": 17, + "src/main/rate-limits/service-refresh-orchestration.test.ts": 29, + "src/main/rate-limits/service-window-activation.test.ts": 22, + "src/main/refused-tree-kill-root-termination.test.ts": 24, + "src/main/remote-agent-trust-presets.test.ts": 13, + "src/main/remote-worktree-history-cleanup.test.ts": 12, + "src/main/repo-git-remote-identity-enrichment.test.ts": 22, + "src/main/repo-git-remote-identity.test.ts": 23, + "src/main/repo-git-username-enrichment.test.ts": 11, + "src/main/repo-icon-autodetect.test.ts": 242, + "src/main/repo-icon-file-detection.test.ts": 70, + "src/main/repo-icon-source-href.test.ts": 73, + "src/main/repo-maintenance-idle-gate.test.ts": 5, + "src/main/repo-worktrees.test.ts": 20, + "src/main/runtime/agent-prompt-receipt-correlation.test.ts": 65, + "src/main/runtime/agent-prompt-request-correlation.test.ts": 9, + "src/main/runtime/agent-prompt-submission-runtime.test.ts": 1762, + "src/main/runtime/agent-prompt-submission-verification.test.ts": 125, + "src/main/runtime/agent-prompt-submission-windows-submit-delay.test.ts": 192, + "src/main/runtime/agent-session-backup-recovery.test.ts": 116, + "src/main/runtime/agent-session-claim-identity.test.ts": 12, + "src/main/runtime/agent-session-conversation-name-store.test.ts": 76, + "src/main/runtime/agent-session-eviction-settlement-latch.test.ts": 9, + "src/main/runtime/agent-session-handoff-lease-transitions.test.ts": 8, + "src/main/runtime/agent-session-launch-env-admission.test.ts": 165, + "src/main/runtime/agent-session-launch-env-backfill.test.ts": 40, + "src/main/runtime/agent-session-lease-renewal.test.ts": 221, + "src/main/runtime/agent-session-orphan-child-reaper.test.ts": 12, + "src/main/runtime/agent-session-process-identity-probe-windows-batch.test.ts": 5, + "src/main/runtime/agent-session-process-identity-probe.test.ts": 17, + "src/main/runtime/agent-session-provider-handle-transition.test.ts": 7, + "src/main/runtime/agent-session-pty-write-enforcement.test.ts": 2113, + "src/main/runtime/agent-session-pty-write-gate.test.ts": 15, + "src/main/runtime/agent-session-record-conversation-name.test.ts": 14, + "src/main/runtime/agent-session-record-options.test.ts": 33, + "src/main/runtime/agent-session-record-store-security.test.ts": 23, + "src/main/runtime/agent-session-record-store.test.ts": 744, + "src/main/runtime/agent-session-record-unsupported-schema.test.ts": 26, + "src/main/runtime/agent-session-recovery-publish-fault.test.ts": 25, + "src/main/runtime/agent-session-reservation-admission.test.ts": 9, + "src/main/runtime/agent-session-restart-handoff-adjudication.test.ts": 13, + "src/main/runtime/agent-session-resume-args.test.ts": 5, + "src/main/runtime/agent-session-spawn-token-process-scan.test.ts": 8, + "src/main/runtime/agent-session-spawn-token-readback.test.ts": 9, + "src/main/runtime/agent-session-unreadable-record-salvage.test.ts": 68, + "src/main/runtime/agent-terminal-launch-trust-host.test.ts": 27, + "src/main/runtime/antigravity-readiness-transcripts.test.ts": 9380, + "src/main/runtime/automation-change-publication.test.ts": 4436, + "src/main/runtime/browser-client-download-transfers.test.ts": 80, + "src/main/runtime/browser-execution-host-key-resolution.test.ts": 25, + "src/main/runtime/browser-host-capability-selection.test.ts": 12, + "src/main/runtime/browser-host-client-page-adoption-grants.test.ts": 9, + "src/main/runtime/browser-host-client-page-adoption.test.ts": 9, + "src/main/runtime/browser-host-client-page-creation.test.ts": 28, + "src/main/runtime/browser-host-command-ledger-capacity.test.ts": 14, + "src/main/runtime/browser-host-command-ledger.test.ts": 30, + "src/main/runtime/browser-host-file-channel-admission.test.ts": 7, + "src/main/runtime/browser-host-lease-download-transfer-cleanup.test.ts": 114, + "src/main/runtime/browser-host-lease-fenced-page-release.test.ts": 30, + "src/main/runtime/browser-host-lease-placement-retirement.test.ts": 33, + "src/main/runtime/browser-host-lease-registry.test.ts": 47, + "src/main/runtime/browser-host-page-placement-replacement.test.ts": 11, + "src/main/runtime/browser-host-page-placement.test.ts": 16, + "src/main/runtime/browser-host-page-reconciliation-executor.test.ts": 44, + "src/main/runtime/browser-host-page-reconciliation-orchestration.test.ts": 245, + "src/main/runtime/browser-host-page-reconciliation-plan.test.ts": 22, + "src/main/runtime/browser-host-page-reconciliation-preconditions.test.ts": 7, + "src/main/runtime/browser-host-page-retirement.test.ts": 9, + "src/main/runtime/browser-network-tunnel-paired-runtime.integration.test.ts": 411, + "src/main/runtime/browser-screencast-driver-attribution.test.ts": 503, + "src/main/runtime/browser-screencast-driver-scope.test.ts": 5, + "src/main/runtime/browser-screencast-ghost-subscriber-eviction.test.ts": 14, + "src/main/runtime/browser-screencast-remote-viewer-retention.test.ts": 379, + "src/main/runtime/browser-session-tab-selection-snapshot.test.ts": 12, + "src/main/runtime/browser-tab-create-caller-navigation.test.ts": 641, + "src/main/runtime/browser-tab-create-publication.test.ts": 998, + "src/main/runtime/claude-agent-teams-pty-exit-leak.test.ts": 23, + "src/main/runtime/claude-agent-teams-service.test.ts": 29, + "src/main/runtime/claude-agent-teams-shim-env.test.ts": 41, + "src/main/runtime/claude-structured-session-integration.test.ts": 943, + "src/main/runtime/cli-terminal-create-host-session-binding.test.ts": 22, + "src/main/runtime/client-hosted-browser-page-persistence.integration.test.ts": 39, + "src/main/runtime/client-hosted-browser-page-persistence.test.ts": 24, + "src/main/runtime/client-hosted-browser-row-hydration-census.test.ts": 427, + "src/main/runtime/client-hosted-browser-row-projection.test.ts": 9, + "src/main/runtime/client-hosted-browser-row-publication.test.ts": 30, + "src/main/runtime/client-hosted-browser-row-push.integration.test.ts": 41, + "src/main/runtime/client-hosted-page-reconciliation-hold.integration.test.ts": 16, + "src/main/runtime/client-hosted-page-reconciliation-window.test.ts": 23, + "src/main/runtime/client-session-tab-selection.test.ts": 11, + "src/main/runtime/decorative-title-fact-emission.test.ts": 4, + "src/main/runtime/exit-provenance-audit.test.ts": 394, + "src/main/runtime/expired-ssh-lease-pane-candidacy.test.ts": 22, + "src/main/runtime/external-worktree-paired-client-discovery.integration.test.ts": 203, + "src/main/runtime/fetch-remote-cache.test.ts": 271, + "src/main/runtime/file-watcher-host.test.ts": 600, + "src/main/runtime/fit-override-integration.test.ts": 56, + "src/main/runtime/folder-workspace-pty-identity.test.ts": 54, + "src/main/runtime/graph-sync-deletion-fence.test.ts": 43, + "src/main/runtime/graph-sync-live-daemon-pty-tab-preservation.test.ts": 95, + "src/main/runtime/graph-sync-mobile-snapshot-gating.test.ts": 94, + "src/main/runtime/graph-sync-payload-partition.test.ts": 33, + "src/main/runtime/headless-tab-group-split-layout.test.ts": 15, + "src/main/runtime/headless-tab-order-stability.test.ts": 29, + "src/main/runtime/headless-terminal-dispose-write-ordering.test.ts": 25, + "src/main/runtime/headless-terminal-query-reply-policy.test.ts": 4, + "src/main/runtime/headless-terminal-split-layout.test.ts": 9, + "src/main/runtime/hidden-output-restored-provider-snapshot.test.ts": 74, + "src/main/runtime/host-terminal-close-persistence-durability.test.ts": 59, + "src/main/runtime/linear-save-issue.test.ts": 34, + "src/main/runtime/managed-worktree-create-execution-host.test.ts": 24, + "src/main/runtime/missing-worktree-terminal-reconciliation.test.ts": 14, + "src/main/runtime/mobile-agent-status-permission-renewal.test.ts": 34, + "src/main/runtime/mobile-notification-dismissal-read-failure.test.ts": 4, + "src/main/runtime/mobile-notification-dismissal-store.test.ts": 12, + "src/main/runtime/mobile-notification-replay.test.ts": 11, + "src/main/runtime/mobile-pairing-qr.test.ts": 1086, + "src/main/runtime/mobile-pairing-userdata-path.test.ts": 1112, + "src/main/runtime/mobile-presence-lock.test.ts": 230, + "src/main/runtime/mobile-rpc-allowlist.test.ts": 84, + "src/main/runtime/mobile-session-tabs-agent-status-heartbeat.test.ts": 21, + "src/main/runtime/mobile-session-tabs-churn-coalescing.test.ts": 26, + "src/main/runtime/mobile-session-tabs-notify-coalescer.test.ts": 17, + "src/main/runtime/mobile-session-terminal-persistence-retirement.test.ts": 19, + "src/main/runtime/mobile-session-terminal-retirement-proof.test.ts": 5, + "src/main/runtime/mobile-session-terminal-retirement.test.ts": 12, + "src/main/runtime/mobile-subscribe-integration.test.ts": 303, + "src/main/runtime/multi-client-navigation-isolation.integration.test.ts": 551, + "src/main/runtime/opencode-finished-session-authority.test.ts": 356, + "src/main/runtime/orca-runtime-agent-session-operation.test.ts": 84, + "src/main/runtime/orca-runtime-agent-skill-share.test.ts": 224, + "src/main/runtime/orca-runtime-automations.test.ts": 35, + "src/main/runtime/orca-runtime-browser-client-hosted.test.ts": 778, + "src/main/runtime/orca-runtime-browser-ghost-session-row-close.test.ts": 416, + "src/main/runtime/orca-runtime-browser-headless.test.ts": 450, + "src/main/runtime/orca-runtime-browser-screencast-fanout.test.ts": 316, + "src/main/runtime/orca-runtime-browser.test.ts": 604, + "src/main/runtime/orca-runtime-create-base-prefetch.test.ts": 27, + "src/main/runtime/orca-runtime-emulator-folder-workspace.test.ts": 76, + "src/main/runtime/orca-runtime-exact-worker-provider-session.test.ts": 8, + "src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts": 15, + "src/main/runtime/orca-runtime-files-preview-budget.test.ts": 104, + "src/main/runtime/orca-runtime-files-rename-authority.test.ts": 29, + "src/main/runtime/orca-runtime-files-search.test.ts": 25, + "src/main/runtime/orca-runtime-files-ssh-chunk-reads.test.ts": 68, + "src/main/runtime/orca-runtime-files-ssh-rearm.test.ts": 60, + "src/main/runtime/orca-runtime-files-terminal-artifact-grants.test.ts": 62, + "src/main/runtime/orca-runtime-files-terminal-artifact-io.test.ts": 99, + "src/main/runtime/orca-runtime-files-terminal-link-host-translation.test.ts": 30, + "src/main/runtime/orca-runtime-files-terminal-path-resolution.test.ts": 22, + "src/main/runtime/orca-runtime-files-watch-host-scope.test.ts": 15, + "src/main/runtime/orca-runtime-files-watch.test.ts": 106, + "src/main/runtime/orca-runtime-git-branch-diff.test.ts": 9, + "src/main/runtime/orca-runtime-git-diff-budget.test.ts": 25, + "src/main/runtime/orca-runtime-git.test.ts": 31, + "src/main/runtime/orca-runtime-headless-hydration-repo-gate.test.ts": 32, + "src/main/runtime/orca-runtime-hook-agent-status-projection.test.ts": 297, + "src/main/runtime/orca-runtime-linked-issue-live-meta.integration.test.ts": 17, + "src/main/runtime/orca-runtime-mobile-agent-status-title-truthfulness.test.ts": 44, + "src/main/runtime/orca-runtime-mobile-close-preserved-resurrection.test.ts": 191, + "src/main/runtime/orca-runtime-module-size.test.ts": 20, + "src/main/runtime/orca-runtime-path-candidate-history.test.ts": 16, + "src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts": 27, + "src/main/runtime/orca-runtime-provider-reattach-launch-identity.test.ts": 21, + "src/main/runtime/orca-runtime-skill-recovery.test.ts": 20, + "src/main/runtime/orca-runtime-structured-agent-session-create-intent.test.ts": 21, + "src/main/runtime/orca-runtime-structured-agent-session-launch-args.test.ts": 20, + "src/main/runtime/orca-runtime-structured-claude-account-gate.test.ts": 17, + "src/main/runtime/orca-runtime-structured-claude-gate-wiring.test.ts": 19, + "src/main/runtime/orca-runtime-structured-native-chat-settings.test.ts": 13, + "src/main/runtime/orca-runtime-structured-session-restore.test.ts": 59, + "src/main/runtime/orca-runtime-structured-status-sink-wiring.test.ts": 26, + "src/main/runtime/orca-runtime-structured-tui-tab-binding.test.ts": 302, + "src/main/runtime/orca-runtime-tab-id-collision.test.ts": 18, + "src/main/runtime/orca-runtime-tail-wait-memo.test.ts": 89, + "src/main/runtime/orca-runtime-terminal-close-continuity.test.ts": 231, + "src/main/runtime/orca-runtime-terminal-create-idempotency.test.ts": 77, + "src/main/runtime/orca-runtime-terminal-cwd.test.ts": 62, + "src/main/runtime/orca-runtime-terminal-handle-incarnation.test.ts": 78, + "src/main/runtime/orca-runtime-terminal-retirement-host-partition.test.ts": 54, + "src/main/runtime/orca-runtime-terminal-retirement.test.ts": 255, + "src/main/runtime/orca-runtime-terminal-split-authority.test.ts": 384, + "src/main/runtime/orca-runtime-test-fragment-coverage.test.ts": 5, + "src/main/runtime/orca-runtime.test.ts": 12320, + "src/main/runtime/orchestration-codex-completion-title.test.ts": 664, + "src/main/runtime/orchestration-compatibility-authority.test.ts": 42, + "src/main/runtime/orchestration-dispatch-mailbox-delivery.test.ts": 190, + "src/main/runtime/orchestration-mailbox-cold-park-idle.test.ts": 119, + "src/main/runtime/orchestration-mailbox-crash-recovery.test.ts": 155, + "src/main/runtime/orchestration-mailbox-detached-routing.test.ts": 534, + "src/main/runtime/orchestration-mailbox-filtered-waiters.test.ts": 270, + "src/main/runtime/orchestration-mailbox-notification-consistency.test.ts": 1187, + "src/main/runtime/orchestration-mailbox-pointer-cli-command.test.ts": 107, + "src/main/runtime/orchestration-mailbox-pty-write-gate.test.ts": 162, + "src/main/runtime/orchestration-mailbox-routing-races.test.ts": 642, + "src/main/runtime/orchestration-mailbox-transport-settlement.test.ts": 224, + "src/main/runtime/orchestration-message-delivery-identity.test.ts": 244, + "src/main/runtime/orchestration-messages-fake-parity.test.ts": 55, + "src/main/runtime/orchestration-structured-chat-lease.test.ts": 8461, + "src/main/runtime/orchestration-worker-workspace-resolution.test.ts": 57, + "src/main/runtime/orchestration/adopted-structured-pointer-delivery.test.ts": 13, + "src/main/runtime/orchestration/cli-command.test.ts": 7, + "src/main/runtime/orchestration/coordinator-decision-gates.test.ts": 62, + "src/main/runtime/orchestration/coordinator-dispatch-unobserved-prompt.test.ts": 59, + "src/main/runtime/orchestration/coordinator-drift-probe-coalescing.test.ts": 186, + "src/main/runtime/orchestration/coordinator-escalation-triage.test.ts": 28, + "src/main/runtime/orchestration/coordinator-stale-base-flag.test.ts": 9, + "src/main/runtime/orchestration/coordinator.test.ts": 3333, + "src/main/runtime/orchestration/db-empty-dispatch-shortcircuit.benchmark.test.ts": 64, + "src/main/runtime/orchestration/db-heartbeat-straggler-guard.test.ts": 31, + "src/main/runtime/orchestration/db-message-timestamp.test.ts": 18, + "src/main/runtime/orchestration/db-messages.test.ts": 115, + "src/main/runtime/orchestration/db-stopping-worker-task-guard.test.ts": 97, + "src/main/runtime/orchestration/db-task-create-readiness.test.ts": 174, + "src/main/runtime/orchestration/db-task-dispatch-invariant.test.ts": 521, + "src/main/runtime/orchestration/db-task-dispatch-lifecycle-guards.test.ts": 716, + "src/main/runtime/orchestration/db-task-dispatch-races.test.ts": 172, + "src/main/runtime/orchestration/db-undelivered-mailboxes.test.ts": 44, + "src/main/runtime/orchestration/db.test.ts": 836, + "src/main/runtime/orchestration/db/attempt-outcome-projection.test.ts": 285, + "src/main/runtime/orchestration/db/decision-gate-lifecycle.test.ts": 46, + "src/main/runtime/orchestration/db/dispatch-context/dispatch-capability.test.ts": 51, + "src/main/runtime/orchestration/db/dispatch-depth.test.ts": 320, + "src/main/runtime/orchestration/db/dispatch-mailbox-consumer-fencing.test.ts": 107, + "src/main/runtime/orchestration/db/dispatch-row-writer-boundary.test.ts": 587, + "src/main/runtime/orchestration/db/federated-worker-report-outcome.test.ts": 8, + "src/main/runtime/orchestration/db/federation/federated-dispatch-observation-fence.test.ts": 28, + "src/main/runtime/orchestration/db/federation/remote-dispatch-attachment-release.test.ts": 75, + "src/main/runtime/orchestration/db/hot-path-statement-compilation.test.ts": 74, + "src/main/runtime/orchestration/db/legacy-question-identity.test.ts": 7, + "src/main/runtime/orchestration/db/lifecycle-rejection-marker.test.ts": 8, + "src/main/runtime/orchestration/db/lifecycle-transition-boundary.test.ts": 6, + "src/main/runtime/orchestration/db/lifecycle-transition.test.ts": 76, + "src/main/runtime/orchestration/db/pane-key-match.test.ts": 5, + "src/main/runtime/orchestration/db/row-column-lists.test.ts": 72, + "src/main/runtime/orchestration/db/run-list-cursor.test.ts": 5, + "src/main/runtime/orchestration/db/schema/federated-home-run-migration.test.ts": 128, + "src/main/runtime/orchestration/db/schema/structured-pointer-schema-migration.test.ts": 60, + "src/main/runtime/orchestration/db/writer-run-required.test.ts": 54, + "src/main/runtime/orchestration/dispatch-consumer-generation-migration.test.ts": 143, + "src/main/runtime/orchestration/dispatch-creator-identity-migration.test.ts": 416, + "src/main/runtime/orchestration/dispatch-failure-idempotency.test.ts": 48, + "src/main/runtime/orchestration/federation-ack-checkpoints.test.ts": 6, + "src/main/runtime/orchestration/federation-acknowledgment-integrity.test.ts": 74, + "src/main/runtime/orchestration/federation-acknowledgment-migration.test.ts": 67, + "src/main/runtime/orchestration/federation-lifecycle-settlement.test.ts": 4, + "src/main/runtime/orchestration/federation-sync.test.ts": 196, + "src/main/runtime/orchestration/federation-terminal-recovery.test.ts": 31, + "src/main/runtime/orchestration/formatter.test.ts": 10, + "src/main/runtime/orchestration/groups.test.ts": 8, + "src/main/runtime/orchestration/lifecycle-caller-edges.test.ts": 76, + "src/main/runtime/orchestration/lifecycle-reconciliation.test.ts": 237, + "src/main/runtime/orchestration/lightweight-run-worker-exit-escalation.test.ts": 182, + "src/main/runtime/orchestration/mailbox-pointer-eligibility.test.ts": 67, + "src/main/runtime/orchestration/mailbox-pointer-release-query.test.ts": 120, + "src/main/runtime/orchestration/mailbox-pointer-stage.test.ts": 129, + "src/main/runtime/orchestration/mailbox-pointer-submit.test.ts": 447, + "src/main/runtime/orchestration/message-batch-atomicity.test.ts": 107, + "src/main/runtime/orchestration/mutation-receipt-capacity.test.ts": 155, + "src/main/runtime/orchestration/nested-worker-depth-migration.test.ts": 233, + "src/main/runtime/orchestration/orchestration-adopted-run-binding.test.ts": 643, + "src/main/runtime/orchestration/orchestration-all-start-versions-migration.test.ts": 1759, + "src/main/runtime/orchestration/orchestration-creator-authority-performance.test.ts": 517, + "src/main/runtime/orchestration/orchestration-db-permissions.test.ts": 37, + "src/main/runtime/orchestration/orchestration-db-retention-pagination.test.ts": 408, + "src/main/runtime/orchestration/orchestration-federated-legacy-probe.test.ts": 309, + "src/main/runtime/orchestration/orchestration-legacy-coordinator-authority-db.test.ts": 371, + "src/main/runtime/orchestration/orchestration-legacy-question-migration-db.test.ts": 445, + "src/main/runtime/orchestration/orchestration-legacy-storage-db.test.ts": 1251, + "src/main/runtime/orchestration/orchestration-legacy-worker-terminal-recovery.test.ts": 6, + "src/main/runtime/orchestration/orchestration-mutation-question-db.test.ts": 75, + "src/main/runtime/orchestration/orchestration-peer-capability-cache.test.ts": 11, + "src/main/runtime/orchestration/orchestration-reset-db.test.ts": 84, + "src/main/runtime/orchestration/orchestration-run-delivery-db.test.ts": 184, + "src/main/runtime/orchestration/orchestration-run-list-compatibility.test.ts": 32, + "src/main/runtime/orchestration/orchestration-version-skew-migration.test.ts": 539, + "src/main/runtime/orchestration/orchestration-worker-dispatch-db.test.ts": 352, + "src/main/runtime/orchestration/preamble.test.ts": 75, + "src/main/runtime/orchestration/r1-identity-migration.test.ts": 109, + "src/main/runtime/orchestration/run-coordinator-handle-migration.test.ts": 148, + "src/main/runtime/orchestration/settled-question-threads-migration.test.ts": 51, + "src/main/runtime/orchestration/setup-completion-signal.test.ts": 10, + "src/main/runtime/orchestration/structured-mailbox-pointer-delivery.test.ts": 44, + "src/main/runtime/orchestration/structured-mailbox-pointer-host.test.ts": 14, + "src/main/runtime/orchestration/structured-pointer-operation-id.test.ts": 13, + "src/main/runtime/orchestration/structured-session-pointer-delivery.test.ts": 14, + "src/main/runtime/orchestration/structured-worker-direct-mailbox-target.test.ts": 9, + "src/main/runtime/orchestration/structured-worker-group-addressing.test.ts": 17, + "src/main/runtime/orchestration/structured-worker-journal-archive.test.ts": 32, + "src/main/runtime/orchestration/task-deps-flag.test.ts": 15, + "src/main/runtime/orchestration/worker-attention-context.test.ts": 10, + "src/main/runtime/orchestration/worker-output-archive-bounding.test.ts": 58, + "src/main/runtime/orchestration/worker-output-archive.test.ts": 18, + "src/main/runtime/orchestration/worker-output-cursor.test.ts": 8, + "src/main/runtime/orchestration/worker-provider-session.test.ts": 7, + "src/main/runtime/orchestration/worker-start-unobserved-prompt-settlement.test.ts": 63, + "src/main/runtime/orchestration/worker-transcript-payload.test.ts": 15, + "src/main/runtime/orchestration/worker-transcript-read.test.ts": 58, + "src/main/runtime/orchestration/worker-transcript-remote-read.test.ts": 60, + "src/main/runtime/pairing-endpoint.test.ts": 24, + "src/main/runtime/pty-exit-agent-status-reconciliation.test.ts": 35, + "src/main/runtime/pty-exit-per-pty-map-reaper-ratchet.test.ts": 13, + "src/main/runtime/pty-inventory-liveness-verdict.test.ts": 58, + "src/main/runtime/pty-inventory-partial-relay-liveness.test.ts": 31, + "src/main/runtime/pty-shell-ownership-mirror.test.ts": 1507, + "src/main/runtime/pty-transcript-prune-wait-cache.test.ts": 24, + "src/main/runtime/pty-waiver-source-invariant.test.ts": 9, + "src/main/runtime/public-ssh-state.test.ts": 7, + "src/main/runtime/push/desktop-push-service-unreadable-outbox.test.ts": 32, + "src/main/runtime/push/desktop-push-service.test.ts": 79, + "src/main/runtime/push/push-agent-state.test.ts": 9, + "src/main/runtime/push/push-cleanup-auth-expiry.test.ts": 50, + "src/main/runtime/push/push-delivery-policy.test.ts": 9, + "src/main/runtime/push/push-device-registration-persistence.test.ts": 20, + "src/main/runtime/push/push-dispatcher.test.ts": 13, + "src/main/runtime/push/push-gateway-client.test.ts": 149, + "src/main/runtime/push/push-gateway-session.test.ts": 136, + "src/main/runtime/push/push-host-proof-vector.test.ts": 19, + "src/main/runtime/push/push-host-proof.test.ts": 100, + "src/main/runtime/push/push-outcome-counters.test.ts": 6, + "src/main/runtime/push/push-policy-pipeline.integration.test.ts": 38, + "src/main/runtime/push/push-preferences.test.ts": 10, + "src/main/runtime/push/push-registration-races.test.ts": 65, + "src/main/runtime/push/push-registration-rpc.test.ts": 45, + "src/main/runtime/push/push-unpair-persistence.test.ts": 37, + "src/main/runtime/push/push-unregister-outbox.test.ts": 12, + "src/main/runtime/quarter-circle-title-send-authorization.test.ts": 16463, + "src/main/runtime/recent-pty-output-buffer.test.ts": 63, + "src/main/runtime/relay/desktop-relay-service-broker-liveness.test.ts": 10, + "src/main/runtime/relay/desktop-relay-service.test.ts": 12, + "src/main/runtime/relay/mobile-relay-e2ee.integration.test.ts": 83, + "src/main/runtime/relay/relay-auth-coordinator-recovery.test.ts": 18, + "src/main/runtime/relay/relay-auth-coordinator.test.ts": 1169, + "src/main/runtime/relay/relay-auth-host-close-reason.test.ts": 67, + "src/main/runtime/relay/relay-control-client.test.ts": 287, + "src/main/runtime/relay/relay-control-close-reason.test.ts": 40, + "src/main/runtime/relay/relay-control-origin.test.ts": 22, + "src/main/runtime/relay/relay-control-request-retirement.test.ts": 23, + "src/main/runtime/relay/relay-demand-ledger.test.ts": 12, + "src/main/runtime/relay/relay-host-proof.test.ts": 40, + "src/main/runtime/relay/relay-http-client.test.ts": 86, + "src/main/runtime/relay/relay-region-correction.test.ts": 89, + "src/main/runtime/relay/relay-region-preference.test.ts": 90, + "src/main/runtime/relay/relay-region-probe-log.test.ts": 79, + "src/main/runtime/relay/relay-region-refresh.test.ts": 17, + "src/main/runtime/relay/relay-renewal-jitter.test.ts": 506, + "src/main/runtime/relay/relay-revoke-outbox.test.ts": 13, + "src/main/runtime/relay/relay-session-broker.test.ts": 405, + "src/main/runtime/remote-agent-session-host-authority.integration.test.ts": 138, + "src/main/runtime/remote-browser-screencast-frame-admission.test.ts": 47, + "src/main/runtime/remote-desktop-driver.test.ts": 99, + "src/main/runtime/remote-runtime-close-intent.integration.test.ts": 90, + "src/main/runtime/remote-runtime-request-connection.integration.test.ts": 260, + "src/main/runtime/remote-server-updater.test.ts": 9, + "src/main/runtime/renderer-browser-session-reconciliation.test.ts": 11, + "src/main/runtime/repo-icon-fork-backfill.test.ts": 16, + "src/main/runtime/repo-worktree-admin-fingerprint.test.ts": 835, + "src/main/runtime/repo-worktree-resolution-scan.test.ts": 4, + "src/main/runtime/repo-worktree-row-resolution.test.ts": 16, + "src/main/runtime/retained-tail-redraw-window.equivalence.test.ts": 329, + "src/main/runtime/rpc/core-typed-method-contract.test.ts": 9, + "src/main/runtime/rpc/dispatcher-browser-client-automation.test.ts": 23, + "src/main/runtime/rpc/dispatcher-computer-errors.test.ts": 20, + "src/main/runtime/rpc/dispatcher-feature-interactions.test.ts": 17, + "src/main/runtime/rpc/dispatcher-request-parsing.test.ts": 18, + "src/main/runtime/rpc/e2ee-channel-text-backpressure.test.ts": 55, + "src/main/runtime/rpc/e2ee-channel-v2.test.ts": 81, + "src/main/runtime/rpc/e2ee-channel.test.ts": 196, + "src/main/runtime/rpc/e2ee-crypto.test.ts": 41, + "src/main/runtime/rpc/e2ee-integration.test.ts": 57, + "src/main/runtime/rpc/errors.test.ts": 22, + "src/main/runtime/rpc/methods/accounts.test.ts": 21, + "src/main/runtime/rpc/methods/agent-hooks.test.ts": 12, + "src/main/runtime/rpc/methods/agent-session.test.ts": 38, + "src/main/runtime/rpc/methods/agent-skill-sharing-capability-grant.test.ts": 25, + "src/main/runtime/rpc/methods/ai-vault.test.ts": 156, + "src/main/runtime/rpc/methods/artifact-sharing-capability-grant.test.ts": 28, + "src/main/runtime/rpc/methods/artifacts.test.ts": 280, + "src/main/runtime/rpc/methods/automation-scoped-list-methods.test.ts": 31, + "src/main/runtime/rpc/methods/automations.test.ts": 45, + "src/main/runtime/rpc/methods/browser-client-host-attach-adoption.test.ts": 236, + "src/main/runtime/rpc/methods/browser-client-host-reconciliation.test.ts": 26, + "src/main/runtime/rpc/methods/browser-client-host.test.ts": 30, + "src/main/runtime/rpc/methods/browser-client-page-metadata.test.ts": 20, + "src/main/runtime/rpc/methods/browser-network-tunnel.test.ts": 233, + "src/main/runtime/rpc/methods/browser-tab-create-schema.test.ts": 12, + "src/main/runtime/rpc/methods/browser.test.ts": 58, + "src/main/runtime/rpc/methods/client-events.test.ts": 7, + "src/main/runtime/rpc/methods/client-native-chat-settings.test.ts": 22, + "src/main/runtime/rpc/methods/client-ui-pairing-local-fields.test.ts": 41, + "src/main/runtime/rpc/methods/client-ui-task-resume-state.test.ts": 26, + "src/main/runtime/rpc/methods/client-ui.test.ts": 96, + "src/main/runtime/rpc/methods/clipboard.test.ts": 44, + "src/main/runtime/rpc/methods/computer-actions.test.ts": 15, + "src/main/runtime/rpc/methods/computer.test.ts": 33, + "src/main/runtime/rpc/methods/diagnostics.test.ts": 8, + "src/main/runtime/rpc/methods/file-watch-event-batcher.test.ts": 26, + "src/main/runtime/rpc/methods/files-doc-preview.test.ts": 13, + "src/main/runtime/rpc/methods/files-list-all-page-size.test.ts": 17, + "src/main/runtime/rpc/methods/files-path-search.test.ts": 150, + "src/main/runtime/rpc/methods/files-preview-transport-budget.test.ts": 17, + "src/main/runtime/rpc/methods/files-terminal-path-resolution.test.ts": 20, + "src/main/runtime/rpc/methods/files-watch-cancellation.test.ts": 68, + "src/main/runtime/rpc/methods/files-watch-cleanup.test.ts": 64, + "src/main/runtime/rpc/methods/files.test.ts": 196, + "src/main/runtime/rpc/methods/git-diff-transport-budget.test.ts": 50, + "src/main/runtime/rpc/methods/git.test.ts": 72, + "src/main/runtime/rpc/methods/github-pr-refresh-reason.test.ts": 14, + "src/main/runtime/rpc/methods/github.test.ts": 65, + "src/main/runtime/rpc/methods/gitlab.test.ts": 58, + "src/main/runtime/rpc/methods/host-capabilities.test.ts": 10, + "src/main/runtime/rpc/methods/hosted-review.test.ts": 25, + "src/main/runtime/rpc/methods/jira.test.ts": 34, + "src/main/runtime/rpc/methods/linear-agent-access.test.ts": 77, + "src/main/runtime/rpc/methods/linear-agent-project-access.test.ts": 708, + "src/main/runtime/rpc/methods/linear.test.ts": 47, + "src/main/runtime/rpc/methods/native-chat-rpc-block-sanitize.test.ts": 6, + "src/main/runtime/rpc/methods/native-chat.test.ts": 29, + "src/main/runtime/rpc/methods/notification-preferences.test.ts": 6, + "src/main/runtime/rpc/methods/notification-reconnect-cooldown.test.ts": 10, + "src/main/runtime/rpc/methods/orchestration-dispatch-error-codes.test.ts": 105, + "src/main/runtime/rpc/methods/orchestration-structured-worker-abandon.test.ts": 69, + "src/main/runtime/rpc/methods/orchestration-structured-worker-lifecycle.test.ts": 14, + "src/main/runtime/rpc/methods/orchestration-structured-worker-redrive.test.ts": 88, + "src/main/runtime/rpc/methods/orchestration-structured-worker-session.test.ts": 16, + "src/main/runtime/rpc/methods/orchestration-structured-worker-start-failure.test.ts": 13, + "src/main/runtime/rpc/methods/orchestration-worker-mode-opacity.test.ts": 89, + "src/main/runtime/rpc/methods/orchestration-worker-start-mode-selection.test.ts": 188, + "src/main/runtime/rpc/methods/orchestration-worker-start-mode.test.ts": 9, + "src/main/runtime/rpc/methods/orchestration-worker-support-unknown.test.ts": 8, + "src/main/runtime/rpc/methods/orchestration/cli-runtime-boundary.test.ts": 96, + "src/main/runtime/rpc/methods/orchestration/federation/federated-attach-receipt.test.ts": 5, + "src/main/runtime/rpc/methods/orchestration/federation/federated-fleet-snapshot.test.ts": 30, + "src/main/runtime/rpc/methods/orchestration/federation/federated-message-targeting.test.ts": 68, + "src/main/runtime/rpc/methods/orchestration/federation/federated-release-safety.test.ts": 105, + "src/main/runtime/rpc/methods/orchestration/federation/federated-transport-safety.test.ts": 31, + "src/main/runtime/rpc/methods/orchestration/federation/federated-worker-start-receipt.test.ts": 50, + "src/main/runtime/rpc/methods/orchestration/federation/federation-agent-launch.test.ts": 60, + "src/main/runtime/rpc/methods/orchestration/federation/federation-control-mail.test.ts": 281, + "src/main/runtime/rpc/methods/orchestration/federation/federation-effects.test.ts": 4, + "src/main/runtime/rpc/methods/orchestration/federation/federation-folder-placement.test.ts": 35, + "src/main/runtime/rpc/methods/orchestration/federation/federation-lifecycle-settlement.test.ts": 1435, + "src/main/runtime/rpc/methods/orchestration/federation/federation-liveness-verdict.test.ts": 279, + "src/main/runtime/rpc/methods/orchestration/federation/federation-output.test.ts": 755, + "src/main/runtime/rpc/methods/orchestration/federation/federation-setup.test.ts": 129, + "src/main/runtime/rpc/methods/orchestration/federation/federation-start-prompt-budget.test.ts": 31, + "src/main/runtime/rpc/methods/orchestration/federation/federation-start-schema.test.ts": 11, + "src/main/runtime/rpc/methods/orchestration/federation/federation.test.ts": 1549, + "src/main/runtime/rpc/methods/orchestration/gates/gate-run-authorization.test.ts": 676, + "src/main/runtime/rpc/methods/orchestration/gates/gates.test.ts": 164, + "src/main/runtime/rpc/methods/orchestration/messaging/ask.test.ts": 430, + "src/main/runtime/rpc/methods/orchestration/messaging/check-superseded-terminal.test.ts": 136, + "src/main/runtime/rpc/methods/orchestration/messaging/check-worker-consumer-fencing.test.ts": 242, + "src/main/runtime/rpc/methods/orchestration/messaging/check-worker-federated-attachment.test.ts": 453, + "src/main/runtime/rpc/methods/orchestration/messaging/check.test.ts": 473, + "src/main/runtime/rpc/methods/orchestration/messaging/recipient-routing.test.ts": 238, + "src/main/runtime/rpc/methods/orchestration/messaging/send-dispatch-authority.test.ts": 138, + "src/main/runtime/rpc/methods/orchestration/messaging/send-group.test.ts": 387, + "src/main/runtime/rpc/methods/orchestration/messaging/send-invalid-type.test.ts": 55, + "src/main/runtime/rpc/methods/orchestration/messaging/send-receipt-plumbing.test.ts": 64, + "src/main/runtime/rpc/methods/orchestration/messaging/send-unbound-terminals.test.ts": 49, + "src/main/runtime/rpc/methods/orchestration/messaging/send.test.ts": 570, + "src/main/runtime/rpc/methods/orchestration/messaging/settled-dispatch-mail.test.ts": 100, + "src/main/runtime/rpc/methods/orchestration/runs/migration-behavior.test.ts": 156, + "src/main/runtime/rpc/methods/orchestration/runs/run-receipt.test.ts": 7, + "src/main/runtime/rpc/methods/orchestration/runs/runs.test.ts": 329, + "src/main/runtime/rpc/methods/orchestration/runs/tasks-dispatch.test.ts": 394, + "src/main/runtime/rpc/methods/orchestration/worker/agent-status-producer-census.test.ts": 1032, + "src/main/runtime/rpc/methods/orchestration/worker/composed-workers.test.ts": 364, + "src/main/runtime/rpc/methods/orchestration/worker/context-only-dispatch-retry.test.ts": 84, + "src/main/runtime/rpc/methods/orchestration/worker/fleet-status-observed-identity.test.ts": 15, + "src/main/runtime/rpc/methods/orchestration/worker/fleet-status-terminal-identity.test.ts": 9, + "src/main/runtime/rpc/methods/orchestration/worker/legacy-dispatch-projection.test.ts": 154, + "src/main/runtime/rpc/methods/orchestration/worker/manual-dispatch-observation.test.ts": 133, + "src/main/runtime/rpc/methods/orchestration/worker/manual-dispatch-release.test.ts": 105, + "src/main/runtime/rpc/methods/orchestration/worker/self-dispatch-nesting-depth.test.ts": 55, + "src/main/runtime/rpc/methods/orchestration/worker/structured-worker-launch-seed-options.test.ts": 10, + "src/main/runtime/rpc/methods/orchestration/worker/worker-interactive-wait.test.ts": 111, + "src/main/runtime/rpc/methods/orchestration/worker/worker-launch-preferences.test.ts": 32, + "src/main/runtime/rpc/methods/orchestration/worker/worker-list-pagination.test.ts": 215, + "src/main/runtime/rpc/methods/orchestration/worker/worker-list-run-scope-rpc.test.ts": 81, + "src/main/runtime/rpc/methods/orchestration/worker/worker-observation.test.ts": 16, + "src/main/runtime/rpc/methods/orchestration/worker/worker-output.test.ts": 36, + "src/main/runtime/rpc/methods/orchestration/worker/worker-read-projection.test.ts": 60, + "src/main/runtime/rpc/methods/orchestration/worker/worker-release-archive.test.ts": 213, + "src/main/runtime/rpc/methods/orchestration/worker/worker-release-inventory.test.ts": 285, + "src/main/runtime/rpc/methods/orchestration/worker/worker-release-liveness-verdict.test.ts": 17, + "src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts": 283, + "src/main/runtime/rpc/methods/orchestration/worker/worker-release-ownership-guard.test.ts": 151, + "src/main/runtime/rpc/methods/orchestration/worker/worker-release-recovery.test.ts": 390, + "src/main/runtime/rpc/methods/orchestration/worker/worker-release.test.ts": 651, + "src/main/runtime/rpc/methods/orchestration/worker/worker-start-budgets.test.ts": 5, + "src/main/runtime/rpc/methods/orchestration/worker/worker-start-outcome-classification.test.ts": 8, + "src/main/runtime/rpc/methods/orchestration/worker/worker-start-prompt-budget.test.ts": 70, + "src/main/runtime/rpc/methods/orchestration/worker/worker-start-prompt-contract.test.ts": 355, + "src/main/runtime/rpc/methods/orchestration/worker/worker-start-terminal-target.test.ts": 106, + "src/main/runtime/rpc/methods/orchestration/worker/worker-start-turn-observation.test.ts": 10, + "src/main/runtime/rpc/methods/orchestration/worker/worker-stop-capability.test.ts": 14, + "src/main/runtime/rpc/methods/orchestration/worker/worker-stop-exit-race.test.ts": 109, + "src/main/runtime/rpc/methods/orchestration/worker/worker-stop-liveness-verdict.test.ts": 136, + "src/main/runtime/rpc/methods/orchestration/worker/worker-terminal-custody-at-creation.test.ts": 505, + "src/main/runtime/rpc/methods/orchestration/worker/workers-new-worktree.test.ts": 735, + "src/main/runtime/rpc/methods/orchestration/worker/workers-recovery.test.ts": 238, + "src/main/runtime/rpc/methods/paired-caller-host-id.test.ts": 8, + "src/main/runtime/rpc/methods/pairing.test.ts": 29, + "src/main/runtime/rpc/methods/plugins.test.ts": 13, + "src/main/runtime/rpc/methods/preflight.test.ts": 14, + "src/main/runtime/rpc/methods/project-host-setup-self-host-stamp.test.ts": 16, + "src/main/runtime/rpc/methods/repo-badge-color.test.ts": 14, + "src/main/runtime/rpc/methods/repo.test.ts": 48, + "src/main/runtime/rpc/methods/runtime-client-capabilities.test.ts": 14, + "src/main/runtime/rpc/methods/session-tab-agent-capability-mutations.test.ts": 25, + "src/main/runtime/rpc/methods/session-tab-agent-status-projection.test.ts": 14, + "src/main/runtime/rpc/methods/session-tab-browser-placement-mutations.test.ts": 18, + "src/main/runtime/rpc/methods/session-tab-browser-placement-projection.test.ts": 14, + "src/main/runtime/rpc/methods/session-tabs-inventory-census-race.test.ts": 239, + "src/main/runtime/rpc/methods/session-tabs-inventory-rpc.test.ts": 41, + "src/main/runtime/rpc/methods/session-tabs-move-validation.test.ts": 41, + "src/main/runtime/rpc/methods/session-tabs-retirement-proof-delta.test.ts": 20, + "src/main/runtime/rpc/methods/session-tabs-schemas.test.ts": 18, + "src/main/runtime/rpc/methods/session-tabs-structured-restore.test.ts": 13, + "src/main/runtime/rpc/methods/session-tabs-unsubscribe.test.ts": 16, + "src/main/runtime/rpc/methods/session-tabs.test.ts": 37, + "src/main/runtime/rpc/methods/skills.test.ts": 26, + "src/main/runtime/rpc/methods/speech.test.ts": 20, + "src/main/runtime/rpc/methods/ssh.test.ts": 17, + "src/main/runtime/rpc/methods/structured-agent-session-admission.test.ts": 47, + "src/main/runtime/rpc/methods/structured-agent-session-adoption-replay.test.ts": 87, + "src/main/runtime/rpc/methods/structured-agent-session-background-task-capability.test.ts": 24, + "src/main/runtime/rpc/methods/structured-agent-session-hold.test.ts": 954, + "src/main/runtime/rpc/methods/structured-agent-session-policy.test.ts": 6, + "src/main/runtime/rpc/methods/structured-agent-session-precommit-refusal.test.ts": 23, + "src/main/runtime/rpc/methods/structured-agent-session-turn-item-capability.test.ts": 20, + "src/main/runtime/rpc/methods/structured-agent-session.test.ts": 127, + "src/main/runtime/rpc/methods/structured-worker-read-cursor.test.ts": 10, + "src/main/runtime/rpc/methods/structured-worker-stop-receipt.test.ts": 56, + "src/main/runtime/rpc/methods/structured-worker-tab-retirement.test.ts": 37, + "src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts": 14, + "src/main/runtime/rpc/methods/terminal-legacy-stream-id-characterization.test.ts": 165, + "src/main/runtime/rpc/methods/terminal-manifest-characterization.test.ts": 40, + "src/main/runtime/rpc/methods/terminal-navigation.test.ts": 15, + "src/main/runtime/rpc/methods/terminal-read-screen-cursor.test.ts": 10, + "src/main/runtime/rpc/methods/terminal-stream-extraction-characterization.test.ts": 20, + "src/main/runtime/rpc/methods/terminal/terminal-inspect-process-params.test.ts": 7, + "src/main/runtime/rpc/methods/updater.test.ts": 7, + "src/main/runtime/rpc/methods/workspace-cleanup-ui-schema.test.ts": 17, + "src/main/runtime/rpc/methods/workspace-ports.test.ts": 19, + "src/main/runtime/rpc/methods/worktree-catalog-snapshot-method.test.ts": 17, + "src/main/runtime/rpc/methods/worktree-create-args.test.ts": 14, + "src/main/runtime/rpc/methods/worktree-create-navigation.test.ts": 26, + "src/main/runtime/rpc/methods/worktree-github-pr-suppression.test.ts": 20, + "src/main/runtime/rpc/methods/worktree-missing-terminal-teardown.test.ts": 13, + "src/main/runtime/rpc/methods/worktree-retired-names.test.ts": 12, + "src/main/runtime/rpc/methods/worktree-rm-host-qualification.test.ts": 26, + "src/main/runtime/rpc/methods/worktree-rm-pty-waiver.test.ts": 10, + "src/main/runtime/rpc/methods/worktree-schemas.test.ts": 16, + "src/main/runtime/rpc/methods/worktree.test.ts": 70, + "src/main/runtime/rpc/mobile-auth-acl-critical-path.test.ts": 419, + "src/main/runtime/rpc/mobile-clipboard-image-provenance.test.ts": 25, + "src/main/runtime/rpc/mobile-e2ee-outbound-memory-budget.test.ts": 7, + "src/main/runtime/rpc/mobile-e2ee-v2-desktop-session.test.ts": 33, + "src/main/runtime/rpc/mobile-e2ee-v2-key-schedule.test.ts": 46, + "src/main/runtime/rpc/mobile-socket-wiring.test.ts": 117, + "src/main/runtime/rpc/orchestration-11745-regression-verification.test.ts": 1475, + "src/main/runtime/rpc/orchestration-commit-notify-characterization.test.ts": 326, + "src/main/runtime/rpc/orchestration-contract-fence.test.ts": 121, + "src/main/runtime/rpc/orchestration-current-authority-precedence.test.ts": 657, + "src/main/runtime/rpc/orchestration-legacy-compatibility-dispatcher.test.ts": 3011, + "src/main/runtime/rpc/orchestration-legacy-coordinator-race.test.ts": 963, + "src/main/runtime/rpc/orchestration-legacy-fence-jurisdiction.test.ts": 3247, + "src/main/runtime/rpc/orchestration-legacy-question-takeover.test.ts": 293, + "src/main/runtime/rpc/orchestration-legacy-run-routing.test.ts": 363, + "src/main/runtime/rpc/orchestration-legacy-takeover-current-authority.test.ts": 152, + "src/main/runtime/rpc/orchestration-legacy-takeover-delivery.test.ts": 199, + "src/main/runtime/rpc/orchestration-legacy-takeover-dispatcher.test.ts": 498, + "src/main/runtime/rpc/orchestration-mutation-executor.test.ts": 186, + "src/main/runtime/rpc/orchestration-mutation-ledger.test.ts": 304, + "src/main/runtime/rpc/orchestration-mutation-request-show-legacy.test.ts": 94, + "src/main/runtime/rpc/orchestration-mutation-request-show.test.ts": 158, + "src/main/runtime/rpc/orchestration-runtime-update-settlement.test.ts": 293, + "src/main/runtime/rpc/orchestration-task-dispatch-invariant.test.ts": 332, + "src/main/runtime/rpc/relay-transport.test.ts": 146, + "src/main/runtime/rpc/remote-runtime-server-heartbeat-missed-probe-tolerance.test.ts": 23, + "src/main/runtime/rpc/remote-runtime-server-heartbeat.test.ts": 9, + "src/main/runtime/rpc/runtime-client-capabilities.test.ts": 6, + "src/main/runtime/rpc/runtime-close-attribution-topology.test.ts": 33, + "src/main/runtime/rpc/runtime-close-attribution.test.ts": 19, + "src/main/runtime/rpc/schemas.test.ts": 38, + "src/main/runtime/rpc/streaming.test.ts": 16, + "src/main/runtime/rpc/terminal-agent-prompt-send.test.ts": 20, + "src/main/runtime/rpc/terminal-agent-send-guard.test.ts": 39, + "src/main/runtime/rpc/terminal-geometry-stale-handle.test.ts": 22, + "src/main/runtime/rpc/terminal-lease-stale-handle-survival.test.ts": 168, + "src/main/runtime/rpc/terminal-list-host-scope-transport.test.ts": 11, + "src/main/runtime/rpc/terminal-multiplex-ack-output-budget.test.ts": 563, + "src/main/runtime/rpc/terminal-multiplex-ack-overflow-recovery.test.ts": 1720, + "src/main/runtime/rpc/terminal-multiplex-desktop-resize-routing.test.ts": 695, + "src/main/runtime/rpc/terminal-multiplex-end-verdict.test.ts": 328, + "src/main/runtime/rpc/terminal-multiplex-escape-tail.test.ts": 27, + "src/main/runtime/rpc/terminal-multiplex-flow-control.bench.test.ts": 22, + "src/main/runtime/rpc/terminal-multiplex-initial-snapshot-buffering.test.ts": 294, + "src/main/runtime/rpc/terminal-multiplex-input-write-rejection.test.ts": 809, + "src/main/runtime/rpc/terminal-multiplex-output-pause-and-viewport.test.ts": 455, + "src/main/runtime/rpc/terminal-multiplex-pty-wait-capacity.test.ts": 446, + "src/main/runtime/rpc/terminal-multiplex-resync-replay-trim.test.ts": 16, + "src/main/runtime/rpc/terminal-multiplex-round-robin.test.ts": 6, + "src/main/runtime/rpc/terminal-multiplex-snapshot-serialization.test.ts": 644, + "src/main/runtime/rpc/terminal-multiplex-source-range-admission.test.ts": 799, + "src/main/runtime/rpc/terminal-multiplex-subscribe-slot-recovery.test.ts": 177, + "src/main/runtime/rpc/terminal-opencode-send-guard.integration.test.ts": 42, + "src/main/runtime/rpc/terminal-output-batching.test.ts": 366, + "src/main/runtime/rpc/terminal-output-frame-chunks-equivalence.test.ts": 14856, + "src/main/runtime/rpc/terminal-output-frame-source-ranges.test.ts": 11, + "src/main/runtime/rpc/terminal-prompt-delivery-receipt.test.ts": 318, + "src/main/runtime/rpc/terminal-provider-snapshot-sequence.test.ts": 220, + "src/main/runtime/rpc/terminal-requested-snapshot-unavailability.test.ts": 531, + "src/main/runtime/rpc/terminal-send-agent-session-lease.test.ts": 27, + "src/main/runtime/rpc/terminal-send-launch-draft-resolution.test.ts": 18, + "src/main/runtime/rpc/terminal-send.test.ts": 284, + "src/main/runtime/rpc/terminal-source-range-ledger.test.ts": 11, + "src/main/runtime/rpc/terminal-stream-byte-length.test.ts": 5718, + "src/main/runtime/rpc/terminal-subscribe-blank-mount.test.ts": 66, + "src/main/runtime/rpc/terminal-subscribe-buffer.test.ts": 605, + "src/main/runtime/rpc/terminal-subscribe-lease-only.test.ts": 66, + "src/main/runtime/rpc/terminal-subscribe-mount-replay.test.ts": 226, + "src/main/runtime/rpc/terminal-subscribe-ownership.test.ts": 226, + "src/main/runtime/rpc/terminal-subscribe-reconnect-rebind.test.ts": 139, + "src/main/runtime/rpc/terminal-subscribe-relay-drop-lease-survival.test.ts": 178, + "src/main/runtime/rpc/terminal-subscribe-renderer-recovery-output.test.ts": 66, + "src/main/runtime/rpc/unix-socket-transport.test.ts": 8, + "src/main/runtime/rpc/unpaired-device-auth-throttle.test.ts": 9, + "src/main/runtime/rpc/worktree-catalog-snapshot.test.ts": 8, + "src/main/runtime/rpc/ws-fallback-port-store.test.ts": 4, + "src/main/runtime/rpc/ws-transport-accept-order.test.ts": 17, + "src/main/runtime/rpc/ws-transport-static-web.test.ts": 6055, + "src/main/runtime/rpc/ws-transport-transient-packet-loss.test.ts": 635, + "src/main/runtime/rpc/ws-transport.test.ts": 976, + "src/main/runtime/runtime-binary-message-router.test.ts": 4, + "src/main/runtime/runtime-browser-client-automation.test.ts": 19, + "src/main/runtime/runtime-browser-client-page-adoption.test.ts": 32, + "src/main/runtime/runtime-browser-client-page-creation.test.ts": 20, + "src/main/runtime/runtime-browser-client-page-recovery.test.ts": 28, + "src/main/runtime/runtime-browser-client-page-restored-recovery.test.ts": 14, + "src/main/runtime/runtime-browser-network-execution-host.test.ts": 7, + "src/main/runtime/runtime-browser-page-registry.test.ts": 13, + "src/main/runtime/runtime-client-settings-minimax-projection.test.ts": 7, + "src/main/runtime/runtime-extraction-regressions.test.ts": 20, + "src/main/runtime/runtime-file-target-connection-field-ratchet.test.ts": 11, + "src/main/runtime/runtime-file-target-execution-host.test.ts": 33, + "src/main/runtime/runtime-folder-workspace.test.ts": 10, + "src/main/runtime/runtime-git-api-contract.test.ts": 7, + "src/main/runtime/runtime-git-branch-compare-admission.test.ts": 7, + "src/main/runtime/runtime-git-command-target.test.ts": 8, + "src/main/runtime/runtime-git-conflict-operation-routing.test.ts": 11, + "src/main/runtime/runtime-git-execution-host-ownership.test.ts": 10, + "src/main/runtime/runtime-git-generation-admission.test.ts": 11, + "src/main/runtime/runtime-git-status-admission.test.ts": 14, + "src/main/runtime/runtime-git-sync-commands.test.ts": 10, + "src/main/runtime/runtime-git-target-execution-host.test.ts": 33, + "src/main/runtime/runtime-graph-reload-lifecycle.test.ts": 10, + "src/main/runtime/runtime-hook-agent-row-selection.test.ts": 11, + "src/main/runtime/runtime-linear-read-commands.test.ts": 9, + "src/main/runtime/runtime-local-worktree-materialization.test.ts": 7, + "src/main/runtime/runtime-local-worktree-terminal-startup.test.ts": 6, + "src/main/runtime/runtime-managed-worktree-metadata-sweep.test.ts": 29, + "src/main/runtime/runtime-managed-worktree-metadata.test.ts": 7, + "src/main/runtime/runtime-managed-worktree-queries.test.ts": 22, + "src/main/runtime/runtime-metadata-ownership-watch.test.ts": 32, + "src/main/runtime/runtime-metadata.test.ts": 38, + "src/main/runtime/runtime-mobile-agent-status-builder.test.ts": 5, + "src/main/runtime/runtime-mobile-file-path-search.test.ts": 15, + "src/main/runtime/runtime-owned-terminal-publication-lineage.test.ts": 28, + "src/main/runtime/runtime-project-group-controller-folder-delete.test.ts": 7, + "src/main/runtime/runtime-project-host-setup-controller.test.ts": 16, + "src/main/runtime/runtime-remote-fetch-ref-maintenance.test.ts": 63, + "src/main/runtime/runtime-remove-project-host-scope.test.ts": 23, + "src/main/runtime/runtime-resolved-worktree-cache.test.ts": 6, + "src/main/runtime/runtime-rpc-browser-host-admission.test.ts": 31, + "src/main/runtime/runtime-rpc-device-revocation.test.ts": 99, + "src/main/runtime/runtime-rpc-long-poll-transport.test.ts": 1931, + "src/main/runtime/runtime-rpc-metadata-lifecycle.test.ts": 29, + "src/main/runtime/runtime-rpc-mobile-method-allowlist.test.ts": 53, + "src/main/runtime/runtime-rpc-mobile-native-chat-settings.test.ts": 5, + "src/main/runtime/runtime-rpc-mobile-terminal-streaming.test.ts": 575, + "src/main/runtime/runtime-rpc-orchestration-db-migration.test.ts": 56, + "src/main/runtime/runtime-rpc-pairing-mode-persistence.test.ts": 139, + "src/main/runtime/runtime-rpc-pairing-offer.test.ts": 71, + "src/main/runtime/runtime-rpc-relay-pairing.test.ts": 431, + "src/main/runtime/runtime-rpc-request-authorization.test.ts": 83, + "src/main/runtime/runtime-rpc-startup-failure.test.ts": 19, + "src/main/runtime/runtime-rpc-terminal-list.test.ts": 641, + "src/main/runtime/runtime-rpc-websocket-bind-host.test.ts": 104, + "src/main/runtime/runtime-rpc-websocket-long-poll-caps.test.ts": 67, + "src/main/runtime/runtime-rpc-worktree-queries.test.ts": 73, + "src/main/runtime/runtime-search-line-fragments.test.ts": 9, + "src/main/runtime/runtime-skill-install-authority.test.ts": 4, + "src/main/runtime/runtime-skill-install-commands.test.ts": 8, + "src/main/runtime/runtime-skill-install-queries.test.ts": 15, + "src/main/runtime/runtime-socket-sweep.test.ts": 8, + "src/main/runtime/runtime-terminal-idle-polls.test.ts": 14, + "src/main/runtime/runtime-terminal-orphan-topology-validation.test.ts": 17, + "src/main/runtime/runtime-terminal-spawn-push-target-materialization.test.ts": 9, + "src/main/runtime/runtime-worktree-agent-rows-structured.test.ts": 13, + "src/main/runtime/runtime-worktree-agent-sources.test.ts": 8, + "src/main/runtime/runtime-worktree-agent-startup.test.ts": 11, + "src/main/runtime/runtime-worktree-ps-summaries.test.ts": 7, + "src/main/runtime/runtime-worktree-selection.test.ts": 8, + "src/main/runtime/runtime-worktree-structured-agent-rows-liveness.test.ts": 87, + "src/main/runtime/saved-structured-agent-session-restoration.test.ts": 5, + "src/main/runtime/selected-review-branch.test.ts": 11, + "src/main/runtime/session-tabs-inventory-publication.test.ts": 47, + "src/main/runtime/settled-worker-process-replacement.test.ts": 96, + "src/main/runtime/structured-agent-session-close.test.ts": 10, + "src/main/runtime/structured-agent-session-integration-replay.test.ts": 400, + "src/main/runtime/structured-agent-session-integration.test.ts": 816, + "src/main/runtime/structured-agent-session-pty-binding.test.ts": 16, + "src/main/runtime/structured-agent-session-rollback-compatibility.test.ts": 39, + "src/main/runtime/structured-agent-session-runtime-exit.test.ts": 348, + "src/main/runtime/structured-agent-session-runtime.test.ts": 33, + "src/main/runtime/structured-agent-session-support-probe.test.ts": 25, + "src/main/runtime/structured-claude-auth-policy-wiring.test.ts": 8, + "src/main/runtime/structured-conversation-tab-replacement.test.ts": 6, + "src/main/runtime/structured-session-worktree-teardown.test.ts": 2436, + "src/main/runtime/structured-tui-exit-proof.test.ts": 14, + "src/main/runtime/structured-tui-idle-evidence.test.ts": 5, + "src/main/runtime/structured-tui-process-identity.test.ts": 26, + "src/main/runtime/structured-tui-recovery-claim-match.test.ts": 13, + "src/main/runtime/structured-worker-agent-presence.test.ts": 6, + "src/main/runtime/structured-worker-authority.test.ts": 7, + "src/main/runtime/structured-worker-child-identity-env.test.ts": 6, + "src/main/runtime/structured-worker-hook-attestation.test.ts": 7, + "src/main/runtime/structured-worker-identity.test.ts": 17, + "src/main/runtime/structured-worker-mail-routing.test.ts": 9, + "src/main/runtime/structured-worker-takeover-pane-key.test.ts": 6, + "src/main/runtime/structured-worker-terminal-read.test.ts": 14, + "src/main/runtime/structured-worker-terminal-refusal.test.ts": 8, + "src/main/runtime/terminal-ansi-pending-retention.test.ts": 464, + "src/main/runtime/terminal-focus-navigation-coalescer.test.ts": 21, + "src/main/runtime/terminal-identity-probe.test.ts": 8, + "src/main/runtime/terminal-interactive-wait-visibility.test.ts": 6198, + "src/main/runtime/terminal-leaf-tab-resolution.test.ts": 6, + "src/main/runtime/terminal-list-execution-host-scope.test.ts": 3128, + "src/main/runtime/terminal-list-payload-size.test.ts": 172, + "src/main/runtime/terminal-list-stale-leaf-liveness.test.ts": 42, + "src/main/runtime/terminal-mobile-subscribe-tab-mount.test.ts": 26, + "src/main/runtime/terminal-model-query-authority.test.ts": 9, + "src/main/runtime/terminal-orphan-owner.test.ts": 7, + "src/main/runtime/terminal-orphan-topology.test.ts": 12, + "src/main/runtime/terminal-pane-recovery-liveness-gate.test.ts": 46, + "src/main/runtime/terminal-projection.test.ts": 6, + "src/main/runtime/terminal-pty-exit-waiter.test.ts": 28, + "src/main/runtime/terminal-query-responder.test.ts": 178, + "src/main/runtime/terminal-restore-record-seed.test.ts": 69, + "src/main/runtime/terminal-retirement-proof-emitted-frame.test.ts": 23, + "src/main/runtime/terminal-retirement-proof-publication.test.ts": 77, + "src/main/runtime/terminal-send-stale-leaf-liveness.test.ts": 180, + "src/main/runtime/terminal-subscribe-exit-waiter-leak.test.ts": 24, + "src/main/runtime/terminal-subscriber-driven-daemon-attach.test.ts": 870, + "src/main/runtime/terminal-tail-buffer.test.ts": 5851, + "src/main/runtime/terminal-tail-row-retention.test.ts": 167, + "src/main/runtime/terminal-tail-sentinel-index.test.ts": 4042, + "src/main/runtime/terminal-tail-whitespace.test.ts": 11, + "src/main/runtime/terminal-vertical-control-scan.test.ts": 8, + "src/main/runtime/terminal-wait-detection.test.ts": 20, + "src/main/runtime/terminal-wait-results.test.ts": 6, + "src/main/runtime/wait-blocked-check-state.test.ts": 112, + "src/main/runtime/wait-blocked-keyword-carry-retention.test.ts": 12, + "src/main/runtime/windows-default-route-interfaces.test.ts": 9, + "src/main/runtime/windows-drive-listing.test.ts": 12, + "src/main/runtime/windows-firewall-remote-scope.test.ts": 19, + "src/main/runtime/windows-mobile-firewall.test.ts": 14, + "src/main/runtime/workspace-session-membership-scaling.test.ts": 13, + "src/main/runtime/worktree-launch-host-repo.test.ts": 10, + "src/main/runtime/worktree-list-host-scope.test.ts": 32, + "src/main/runtime/worktree-path-selector-wsl-posix.test.ts": 46, + "src/main/runtime/worktree-ps-degraded-repo-scan.test.ts": 94, + "src/main/runtime/worktree-ps-host-scope.test.ts": 40, + "src/main/runtime/worktree-rm-id-selector-path-spelling.test.ts": 47, + "src/main/runtime/worktree-scan-admin-fingerprint-gate.test.ts": 531, + "src/main/runtime/worktree-scan-execution-host-routing.test.ts": 33, + "src/main/runtime/worktree-teardown-unstopped-pty.test.ts": 204, + "src/main/runtime/worktree-teardown.test.ts": 235, + "src/main/runtime/worktree-terminal-mutation-lock.test.ts": 13, + "src/main/serve-update-handoff.app-environment.test.ts": 907, + "src/main/serve-update-handoff.test.ts": 8, + "src/main/server/serve-readiness.test.ts": 10, + "src/main/server/serve-stdout-boundary.test.ts": 8, + "src/main/shell-prompt-readiness-probe.test.ts": 28, + "src/main/shell-startup-identity-scanner.test.ts": 6, + "src/main/shell-startup-output-scanner.test.ts": 24, + "src/main/shell-wrapper-content-address.test.ts": 9, + "src/main/shell-wrapper-generated-file-snapshot.test.ts": 40, + "src/main/skills/agent-skill-selection.test.ts": 22, + "src/main/skills/claude-plugin-skill-sources-wsl.test.ts": 6, + "src/main/skills/claude-plugin-skill-sources.test.ts": 7, + "src/main/skills/discovery.test.ts": 108, + "src/main/skills/skill-bundle-artifacts.test.ts": 39, + "src/main/skills/skill-bundle-creation.test.ts": 628, + "src/main/skills/skill-bundle-install-service.test.ts": 196, + "src/main/skills/skill-bundle-observability-summary.test.ts": 4, + "src/main/skills/skill-bundle-ssh-relay-service.test.ts": 105, + "src/main/skills/skill-candidate-concurrency.test.ts": 29, + "src/main/skills/skill-client-mediated-transfer-cancellation.test.ts": 20, + "src/main/skills/skill-client-mediated-transfer.test.ts": 29, + "src/main/skills/skill-cloud-direct-upload.test.ts": 125, + "src/main/skills/skill-cloud-grant-installation.test.ts": 15, + "src/main/skills/skill-cloud-install-target.test.ts": 6, + "src/main/skills/skill-cloud-request.test.ts": 58, + "src/main/skills/skill-cloud-service.test.ts": 73, + "src/main/skills/skill-delete/plan.test.ts": 102, + "src/main/skills/skill-delete/recovery.test.ts": 86, + "src/main/skills/skill-delete/service.test.ts": 618, + "src/main/skills/skill-delete/staging-visibility.test.ts": 21, + "src/main/skills/skill-delete/wsl-enumeration-protocol.test.ts": 12, + "src/main/skills/skill-discovery-concurrency.test.ts": 88, + "src/main/skills/skill-discovery-order.test.ts": 286, + "src/main/skills/skill-discovery-target.test.ts": 21, + "src/main/skills/skill-discovery-wsl-plugins.test.ts": 22, + "src/main/skills/skill-discovery-wsl-script-roundtrip.test.ts": 49, + "src/main/skills/skill-discovery-wsl.test.ts": 40, + "src/main/skills/skill-freshness-eligibility.test.ts": 10, + "src/main/skills/skill-freshness-inventory-limits.test.ts": 6, + "src/main/skills/skill-freshness-inventory.test.ts": 2182, + "src/main/skills/skill-git-tree-identity.test.ts": 40, + "src/main/skills/skill-install-destinations.test.ts": 26, + "src/main/skills/skill-install-discovery-verification.test.ts": 17, + "src/main/skills/skill-install-lock-release.test.ts": 7, + "src/main/skills/skill-install-lock.test.ts": 241, + "src/main/skills/skill-install-management-service.test.ts": 39, + "src/main/skills/skill-install-provenance.test.ts": 19, + "src/main/skills/skill-install-recovery.test.ts": 304, + "src/main/skills/skill-install-request-service.test.ts": 299, + "src/main/skills/skill-install-service.test.ts": 944, + "src/main/skills/skill-install-transaction.test.ts": 949, + "src/main/skills/skill-operation-observability.test.ts": 92, + "src/main/skills/skill-package-creation.test.ts": 312, + "src/main/skills/skill-package-deterministic-gzip.test.ts": 527, + "src/main/skills/skill-package-download.test.ts": 107, + "src/main/skills/skill-package-identity.test.ts": 86, + "src/main/skills/skill-package-tar.test.ts": 259, + "src/main/skills/skill-placement-alias-repair.test.ts": 43, + "src/main/skills/skill-placement-copy-drift.test.ts": 38, + "src/main/skills/skill-placement-reconciliation.test.ts": 74, + "src/main/skills/skill-placement-transaction.test.ts": 97, + "src/main/skills/skill-plugin-cache-scan.test.ts": 599, + "src/main/skills/skill-provider-destinations.test.ts": 8, + "src/main/skills/skill-provider-runtime-roots.test.ts": 9, + "src/main/skills/skill-remote-error-category-parity.test.ts": 15, + "src/main/skills/skill-remote-install-cancellation.test.ts": 4, + "src/main/skills/skill-remote-install-service.test.ts": 32, + "src/main/skills/skill-remove-transaction.test.ts": 577, + "src/main/skills/skill-root-file-walk.test.ts": 27, + "src/main/skills/skill-runtime-capability.test.ts": 9, + "src/main/skills/skill-scan-coalescer.test.ts": 12, + "src/main/skills/skill-share-preparation-service.test.ts": 237, + "src/main/skills/skill-ssh-relay-service.test.ts": 61, + "src/main/skills/skill-transaction-startup-recovery.test.ts": 206, + "src/main/skills/skill-update-convergence.test.ts": 32, + "src/main/skills/skill-update-outcome.test.ts": 49, + "src/main/skills/skill-update-registration.test.ts": 14, + "src/main/skills/skill-update-run.test.ts": 28, + "src/main/skills/skill-upload-session-admission-regression.test.ts": 24, + "src/main/skills/skill-upload-session-service.test.ts": 157, + "src/main/skills/skill-wsl-install-filesystem.test.ts": 9, + "src/main/skills/skill-wsl-provider-detection.test.ts": 10, + "src/main/source-control/forge-provider.test.ts": 52, + "src/main/source-control/hosted-review-azure-devops.integration.test.ts": 153, + "src/main/source-control/hosted-review-base-ref-suffix.test.ts": 12, + "src/main/source-control/hosted-review-bitbucket.integration.test.ts": 368, + "src/main/source-control/hosted-review-branch-cache.test.ts": 186, + "src/main/source-control/hosted-review-creation-eligibility.test.ts": 26, + "src/main/source-control/hosted-review-creation-gitlab-self-hosted.test.ts": 14, + "src/main/source-control/hosted-review-creation-shared-symlinks.test.ts": 17, + "src/main/source-control/hosted-review-creation.test.ts": 29, + "src/main/source-control/hosted-review-dirty-preflight-wsl-paths.test.ts": 5, + "src/main/source-control/hosted-review-execution-host-routing.test.ts": 35, + "src/main/source-control/hosted-review-gitea.integration.test.ts": 321, + "src/main/source-control/hosted-review.test.ts": 14, + "src/main/source-control/pull-request-linked-issue.test.ts": 10, + "src/main/source-control/repo-default-branch.test.ts": 18, + "src/main/source-control/stacked-hosted-review-creation.test.ts": 7, + "src/main/speech/model-catalog.test.ts": 5, + "src/main/speech/model-manager-download-error.test.ts": 17, + "src/main/speech/model-manager-download-resume.test.ts": 3020, + "src/main/speech/model-manager-progress-callback.test.ts": 9, + "src/main/speech/model-manager-stream-cleanup.test.ts": 30, + "src/main/speech/model-manager-windows-path.test.ts": 584, + "src/main/speech/model-manager.test.ts": 45, + "src/main/speech/openai-api-key-store.test.ts": 18, + "src/main/speech/openai-transcription-client.test.ts": 5, + "src/main/speech/speech-model-deletion.test.ts": 8, + "src/main/speech/speech-model-download-response.test.ts": 7, + "src/main/speech/stt-offline-audio-chunker.test.ts": 164, + "src/main/speech/stt-service.test.ts": 24, + "src/main/speech/stt-worker-model-config.test.ts": 8, + "src/main/speech/stt-worker.test.ts": 7, + "src/main/sqlite/sqlite-read-failure.test.ts": 16, + "src/main/sqlite/sync-database.test.ts": 1184, + "src/main/ssh-expired-lease-pane-readoption.test.ts": 58, + "src/main/ssh-reattach-pane-cardinality.test.ts": 181, + "src/main/ssh/orcad-activation-gate.test.ts": 8, + "src/main/ssh/orcad-activation-record.test.ts": 9, + "src/main/ssh/orcad-remote-deploy.test.ts": 63, + "src/main/ssh/orcad-remote-gc.test.ts": 11, + "src/main/ssh/orcad-remote-launch.test.ts": 15, + "src/main/ssh/orcad-remote-rollback.test.ts": 61, + "src/main/ssh/orcad-remote-shell-commands.integration.test.ts": 1254, + "src/main/ssh/orcad-state-snapshot.test.ts": 7, + "src/main/ssh/orcad-update-plan.test.ts": 10, + "src/main/ssh/relay-native-dependency-coverage.test.ts": 333, + "src/main/ssh/relay-protocol-backpressure.test.ts": 31, + "src/main/ssh/relay-protocol.test.ts": 79, + "src/main/ssh/relay-socket-path-limit-shell.integration.test.ts": 77, + "src/main/ssh/relay-socket-path-limit.test.ts": 17, + "src/main/ssh/remote-install-coexistence.test.ts": 22, + "src/main/ssh/remote-install-model.test.ts": 13, + "src/main/ssh/removed-ssh-target-tombstone-retention.test.ts": 7, + "src/main/ssh/sftp-namespace-resolution.test.ts": 36, + "src/main/ssh/sftp-stream-late-error.test.ts": 27, + "src/main/ssh/sftp-upload.test.ts": 139, + "src/main/ssh/ssh-agent-identity-filter.test.ts": 14, + "src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts": 41, + "src/main/ssh/ssh-channel-multiplexer-saturation-wedge.test.ts": 14, + "src/main/ssh/ssh-channel-multiplexer-settlement.test.ts": 8, + "src/main/ssh/ssh-channel-multiplexer.test.ts": 52, + "src/main/ssh/ssh-config-alias-claim.test.ts": 12, + "src/main/ssh/ssh-config-host-picker.test.ts": 199, + "src/main/ssh/ssh-config-loader-regression.test.ts": 80, + "src/main/ssh/ssh-config-loader.test.ts": 77, + "src/main/ssh/ssh-config-parser-host-patterns.test.ts": 7, + "src/main/ssh/ssh-config-parser.test.ts": 262, + "src/main/ssh/ssh-config-resolver.test.ts": 12, + "src/main/ssh/ssh-connect-attempt-cancellation.test.ts": 7, + "src/main/ssh/ssh-connection-auth-fallback.test.ts": 125, + "src/main/ssh/ssh-connection-channel-open.test.ts": 746, + "src/main/ssh/ssh-connection-direct-startup.test.ts": 32, + "src/main/ssh/ssh-connection-file-transfer.test.ts": 17, + "src/main/ssh/ssh-connection-generation.test.ts": 9, + "src/main/ssh/ssh-connection-github-probe.test.ts": 16, + "src/main/ssh/ssh-connection-gssapi-fallback.test.ts": 127, + "src/main/ssh/ssh-connection-host-key-store-wiring.test.ts": 246, + "src/main/ssh/ssh-connection-host-key-verification.test.ts": 66, + "src/main/ssh/ssh-connection-manager-registry.test.ts": 67, + "src/main/ssh/ssh-connection-manager.test.ts": 9, + "src/main/ssh/ssh-connection-reconnect-ladder.test.ts": 335, + "src/main/ssh/ssh-connection-sftp-namespace.test.ts": 834, + "src/main/ssh/ssh-connection-sftp-wire.test.ts": 2413, + "src/main/ssh/ssh-connection-store.test.ts": 36, + "src/main/ssh/ssh-connection-system-transport.test.ts": 58, + "src/main/ssh/ssh-connection-utils.test.ts": 83, + "src/main/ssh/ssh-connection.test.ts": 72, + "src/main/ssh/ssh-control-socket.test.ts": 15, + "src/main/ssh/ssh-file-stream-inactivity-deadline.test.ts": 17, + "src/main/ssh/ssh-file-transfer-abort.test.ts": 16, + "src/main/ssh/ssh-g-config-resolution.test.ts": 67, + "src/main/ssh/ssh-git-response-stream-reader.test.ts": 11, + "src/main/ssh/ssh-git-stream-idle-timer.test.ts": 52, + "src/main/ssh/ssh-host-key-decision.test.ts": 19, + "src/main/ssh/ssh-host-key-store.test.ts": 134, + "src/main/ssh/ssh-host-key-verifier.test.ts": 21, + "src/main/ssh/ssh-known-hosts-source.test.ts": 57, + "src/main/ssh/ssh-known-hosts.test.ts": 23, + "src/main/ssh/ssh-multi-factor-authentication.test.ts": 1247, + "src/main/ssh/ssh-multi-key-authentication.test.ts": 30, + "src/main/ssh/ssh-multiplexer-transport-writer.test.ts": 26, + "src/main/ssh/ssh-orphan-relay-pty-sweep.test.ts": 21, + "src/main/ssh/ssh-orphan-sweep-pane-state-verdicts.test.ts": 21, + "src/main/ssh/ssh-owner-recovery-retry.test.ts": 15, + "src/main/ssh/ssh-pending-pty-kill-replay.test.ts": 13, + "src/main/ssh/ssh-port-forward.test.ts": 25, + "src/main/ssh/ssh-port-scanner.test.ts": 31, + "src/main/ssh/ssh-posix-command-wrapper.test.ts": 27, + "src/main/ssh/ssh-provider-authority.test.ts": 18, + "src/main/ssh/ssh-proxy-command.test.ts": 15, + "src/main/ssh/ssh-pty-consumer-recovery.test.ts": 10, + "src/main/ssh/ssh-pty-consumer-session.test.ts": 13, + "src/main/ssh/ssh-pty-recovery-retention-budget.test.ts": 4, + "src/main/ssh/ssh-pty-retired-source-deliveries.test.ts": 11, + "src/main/ssh/ssh-reconnect-error-classification.test.ts": 10, + "src/main/ssh/ssh-reconnect-ladder.test.ts": 8, + "src/main/ssh/ssh-relay-build-toolchain.test.ts": 12, + "src/main/ssh/ssh-relay-cross-version-isolation.test.ts": 72, + "src/main/ssh/ssh-relay-deploy-helpers.test.ts": 296, + "src/main/ssh/ssh-relay-deploy-incumbent-verdict.test.ts": 16, + "src/main/ssh/ssh-relay-deploy-staged-upload.test.ts": 166, + "src/main/ssh/ssh-relay-deploy.test.ts": 209, + "src/main/ssh/ssh-relay-endpoint-incumbent-shell.integration.test.ts": 2169, + "src/main/ssh/ssh-relay-endpoint-incumbent.test.ts": 15, + "src/main/ssh/ssh-relay-endpoint-takeover.test.ts": 15, + "src/main/ssh/ssh-relay-gc-retry.test.ts": 82, + "src/main/ssh/ssh-relay-install-lock.test.ts": 4, + "src/main/ssh/ssh-relay-install-namespace.test.ts": 16, + "src/main/ssh/ssh-relay-native-deps-cache-deploy.test.ts": 1273, + "src/main/ssh/ssh-relay-native-deps-cache-shell.test.ts": 323, + "src/main/ssh/ssh-relay-native-deps-cache.test.ts": 24, + "src/main/ssh/ssh-relay-native-deps-install-staged-upload.test.ts": 659, + "src/main/ssh/ssh-relay-native-deps-install.test.ts": 13644, + "src/main/ssh/ssh-relay-native-deps-probe-verdict.test.ts": 254, + "src/main/ssh/ssh-relay-node-headers.test.ts": 2346, + "src/main/ssh/ssh-relay-node-pty-repair.test.ts": 15, + "src/main/ssh/ssh-relay-node-pty-spawn-repair.test.ts": 25, + "src/main/ssh/ssh-relay-orphan-abandon-paths.test.ts": 117, + "src/main/ssh/ssh-relay-pty-master-cloexec-install.test.ts": 1911, + "src/main/ssh/ssh-relay-reset.test.ts": 27, + "src/main/ssh/ssh-relay-sentinel-copy-budget.test.ts": 21, + "src/main/ssh/ssh-relay-session-agent-hooks.integration.test.ts": 596, + "src/main/ssh/ssh-relay-session-data-delivery.test.ts": 55, + "src/main/ssh/ssh-relay-session-incarnation.test.ts": 19, + "src/main/ssh/ssh-relay-session-managed-hooks.test.ts": 14, + "src/main/ssh/ssh-relay-session-model-migration.test.ts": 170, + "src/main/ssh/ssh-relay-session-orphan-sweep.test.ts": 43, + "src/main/ssh/ssh-relay-session-pending-kill-replay.test.ts": 14, + "src/main/ssh/ssh-relay-session-reconnect-incarnation.test.ts": 120, + "src/main/ssh/ssh-relay-session-recovery-durability.test.ts": 1066, + "src/main/ssh/ssh-relay-session-recovery-races.test.ts": 325, + "src/main/ssh/ssh-relay-session-rejected-delivery.test.ts": 793, + "src/main/ssh/ssh-relay-session-relay-loss.test.ts": 37, + "src/main/ssh/ssh-relay-session-terminal-error.test.ts": 29, + "src/main/ssh/ssh-relay-session.test.ts": 311, + "src/main/ssh/ssh-relay-sftp-namespace-install.test.ts": 198, + "src/main/ssh/ssh-relay-superseded-endpoints.test.ts": 17, + "src/main/ssh/ssh-relay-upload-stage-commands.test.ts": 39445, + "src/main/ssh/ssh-relay-versioned-install.test.ts": 85, + "src/main/ssh/ssh-remote-cli-dispatch-refusal-passthrough.test.ts": 16, + "src/main/ssh/ssh-remote-cli-format.test.ts": 7, + "src/main/ssh/ssh-remote-cli-host-passthrough.test.ts": 39, + "src/main/ssh/ssh-remote-cli-launcher.test.ts": 10, + "src/main/ssh/ssh-remote-cli-terminal-host-scope.test.ts": 20, + "src/main/ssh/ssh-remote-commands.test.ts": 11428, + "src/main/ssh/ssh-remote-linear-activity-output.test.ts": 4, + "src/main/ssh/ssh-remote-linear-cli.test.ts": 53, + "src/main/ssh/ssh-remote-linear-list-issues.test.ts": 14, + "src/main/ssh/ssh-remote-linear-relation-write.test.ts": 15, + "src/main/ssh/ssh-remote-linear-save-issue.test.ts": 24, + "src/main/ssh/ssh-remote-linear-truncation-output.test.ts": 7, + "src/main/ssh/ssh-remote-node-resolution.test.ts": 118, + "src/main/ssh/ssh-remote-node-toolchain-probe.test.ts": 8, + "src/main/ssh/ssh-remote-orca-cli.test.ts": 140, + "src/main/ssh/ssh-remote-orchestration-compatibility.test.ts": 289, + "src/main/ssh/ssh-remote-orchestration-send.test.ts": 8, + "src/main/ssh/ssh-remote-platform-detection.test.ts": 27, + "src/main/ssh/ssh-remote-platform.test.ts": 16, + "src/main/ssh/ssh-remote-powershell.test.ts": 72, + "src/main/ssh/ssh-remote-windows-command-line-limit.test.ts": 23, + "src/main/ssh/ssh-request-outcome-verdict.test.ts": 5, + "src/main/ssh/ssh-security-key-identity.test.ts": 41, + "src/main/ssh/ssh-session-limit-error.test.ts": 5, + "src/main/ssh/ssh-system-fallback.test.ts": 90, + "src/main/ssh/ssh-system-transport.integration.test.ts": 637, + "src/main/ssh/ssh-target-id-migration.test.ts": 6, + "src/main/ssh/ssh-target-readoption.test.ts": 10, + "src/main/ssh/system-ssh-binary.test.ts": 12, + "src/main/ssh/system-ssh-dynamic-forward-process.test.ts": 61, + "src/main/ssh/system-ssh-forward-process.test.ts": 40, + "src/main/ssh/system-ssh-sftp-args.test.ts": 8, + "src/main/ssh/system-ssh-sftp-path.test.ts": 11, + "src/main/ssh/system-ssh-windows-upload.test.ts": 112, + "src/main/ssh/system-ssh-windows-write-capabilities.test.ts": 8, + "src/main/ssh/vscode-ssh-authority.test.ts": 8, + "src/main/star-nag/service-direct-star.test.ts": 14, + "src/main/star-nag/service-force-show-races.test.ts": 17, + "src/main/star-nag/service-outcome-telemetry.test.ts": 11, + "src/main/star-nag/service-prompt-moments.test.ts": 18, + "src/main/star-nag/service-threshold-prompt.test.ts": 15, + "src/main/startup/bootstrap-fatal-exit-guard.test.ts": 6, + "src/main/startup/branch-rename-hook-structured-session.test.ts": 10, + "src/main/startup/cli-launch-redirect.test.ts": 11, + "src/main/startup/configure-process-dev-parent-shutdown.test.ts": 65, + "src/main/startup/configure-process.test.ts": 123, + "src/main/startup/desktop-startup-ordering.test.ts": 14, + "src/main/startup/dev-education-suppression.test.ts": 10, + "src/main/startup/dev-instance-identity.test.ts": 7, + "src/main/startup/ensure-virtual-display.test.ts": 5092, + "src/main/startup/first-window-startup-services.test.ts": 16, + "src/main/startup/gpu-fallback-marker.test.ts": 7, + "src/main/startup/gpu-fallback-switches.test.ts": 6, + "src/main/startup/gpu-lifecycle-install-dir-acl-guard.test.ts": 55, + "src/main/startup/headless-pty-hydration-ordering.test.ts": 10, + "src/main/startup/host-port-bootstrap-wiring.test.ts": 11, + "src/main/startup/hydrate-shell-path.test.ts": 228, + "src/main/startup/hydrate-shell-path.windows.test.ts": 284, + "src/main/startup/legacy-worker-renderer-recovery.test.ts": 16, + "src/main/startup/login-shell-environment.test.ts": 67, + "src/main/startup/main-process-error-guards.test.ts": 29, + "src/main/startup/main-process-ready-phase-ordering.test.ts": 9, + "src/main/startup/main-window-structured-status-filter.test.ts": 7, + "src/main/startup/os-opened-markdown-delivery.test.ts": 10, + "src/main/startup/os-opened-markdown-files.test.ts": 116, + "src/main/startup/os-opened-markdown-wiring.test.ts": 5, + "src/main/startup/pre-gone-crash-sampling-wiring.test.ts": 5, + "src/main/startup/renderer-heap-headroom.test.ts": 10, + "src/main/startup/run-electron-vite-dev-web.test.ts": 432, + "src/main/startup/run-electron-vite-dev.test.ts": 943, + "src/main/startup/secret-protection-report-deferral-wiring.test.ts": 6, + "src/main/startup/secure-dns-census.test.ts": 407, + "src/main/startup/serve-desktop-activation-wiring.test.ts": 9, + "src/main/startup/serve-desktop-activation.test.ts": 5, + "src/main/startup/serve-mode-argv-cli-redirect-order.test.ts": 9, + "src/main/startup/serve-mode-argv.test.ts": 32, + "src/main/startup/serve-options.test.ts": 20, + "src/main/startup/serve-signal-handlers.test.ts": 4, + "src/main/startup/single-instance-lock-exit.electron.test.ts": 275, + "src/main/startup/single-instance-lock-headless-exit.test.ts": 11, + "src/main/startup/single-instance-lock.test.ts": 19, + "src/main/startup/skill-share-deep-link-state.test.ts": 8, + "src/main/startup/startup-diagnostics.test.ts": 7, + "src/main/startup/window-all-closed-quit-policy.test.ts": 5, + "src/main/startup/windows-desktop-shell-path-startup.test.ts": 8, + "src/main/startup/windows-install-dir-acl-probe.test.ts": 20, + "src/main/startup/windows-install-dir-acl-recovery.test.ts": 460, + "src/main/startup/windows-install-dir-acl-startup-wiring.test.ts": 6, + "src/main/startup/windows-install-dir-package-acl-repair.test.ts": 43, + "src/main/startup/windows-shell-path-hydration.test.ts": 315, + "src/main/startup/windows-shell-path-ownership.test.ts": 10, + "src/main/startup/windows-user-data-acl.test.ts": 15, + "src/main/startup/wsl-cli-reconciliation-startup-barrier.test.ts": 9, + "src/main/stats/agent-session-transition-recorder.test.ts": 23, + "src/main/stats/collector-async-save.test.ts": 375, + "src/main/stats/stats-title-detection-independence.test.ts": 6, + "src/main/synthetic-title-frame-routing.test.ts": 4, + "src/main/synthetic-title-spinner.test.ts": 4, + "src/main/synthetic-title-visibility.test.ts": 5, + "src/main/system-fonts.test.ts": 16, + "src/main/system-power-lifecycle.test.ts": 8, + "src/main/system-resume-broadcast.test.ts": 24, + "src/main/telemetry/burst-cap.test.ts": 27, + "src/main/telemetry/classify-error.test.ts": 7, + "src/main/telemetry/client-lifecycle.test.ts": 17, + "src/main/telemetry/client.test.ts": 42, + "src/main/telemetry/cohort-classifier.test.ts": 13, + "src/main/telemetry/consent.test.ts": 15, + "src/main/telemetry/install-id.test.ts": 7, + "src/main/telemetry/onboarding-cohort-classifier.test.ts": 12, + "src/main/telemetry/onboarding-feature-setup-validator.test.ts": 18, + "src/main/telemetry/validator-warn-cache.test.ts": 30, + "src/main/telemetry/validator.test.ts": 20, + "src/main/terminal-history-async-delete.test.ts": 289, + "src/main/terminal-history-gc-fs-call-count.test.ts": 54, + "src/main/terminal-history-gc.test.ts": 877, + "src/main/terminal-history-tombstone-retry.test.ts": 4798, + "src/main/terminal-history.test.ts": 34, + "src/main/text-generation/agent-failure-output.test.ts": 11, + "src/main/text-generation/commit-message-agent-environment.test.ts": 31, + "src/main/text-generation/commit-message-command-backslash-mode.test.ts": 6, + "src/main/text-generation/commit-message-text-generation-branch-name.test.ts": 16, + "src/main/text-generation/commit-message-text-generation-cancellation.test.ts": 439, + "src/main/text-generation/commit-message-text-generation-failure-sanitization.test.ts": 15, + "src/main/text-generation/commit-message-text-generation-generated-output.test.ts": 15, + "src/main/text-generation/commit-message-text-generation-linked-issue.test.ts": 14, + "src/main/text-generation/commit-message-text-generation-local-subprocess.test.ts": 28, + "src/main/text-generation/commit-message-text-generation-model-discovery.test.ts": 48, + "src/main/text-generation/commit-message-text-generation-regression.test.ts": 10, + "src/main/text-generation/commit-message-text-generation-remote-execution.test.ts": 30, + "src/main/text-generation/commit-message-text-generation-settings.test.ts": 19, + "src/main/text-generation/pull-request-context-errors.test.ts": 25, + "src/main/text-generation/pull-request-context.test.ts": 30, + "src/main/tray/system-tray.test.ts": 92, + "src/main/tray/tray-attention-icon.test.ts": 13, + "src/main/tray/tray-dev-badge.test.ts": 11, + "src/main/update-install-exit-watchdog.test.ts": 12, + "src/main/updater-changelog.test.ts": 7, + "src/main/updater-events.test.ts": 204, + "src/main/updater-lifecycle-diagnostics.test.ts": 5, + "src/main/updater-linux-package-recovery-actions.test.ts": 2079, + "src/main/updater-nudge.test.ts": 12, + "src/main/updater-prerelease-feed-readiness.test.ts": 114, + "src/main/updater-prerelease-feed.test.ts": 148, + "src/main/updater-release-builds.test.ts": 30, + "src/main/updater-test-harness.leaked-timers.test.ts": 2596, + "src/main/updater-test-module-loader.test.ts": 508, + "src/main/updater.build-channel-selection.test.ts": 1674, + "src/main/updater.check-failure.test.ts": 1910, + "src/main/updater.check-preflight.test.ts": 2719, + "src/main/updater.check-settlement.test.ts": 2274, + "src/main/updater.fallback.test.ts": 22, + "src/main/updater.headless-serve-install.test.ts": 272, + "src/main/updater.install-failure-cause.test.ts": 2517, + "src/main/updater.linux-externally-managed.test.ts": 1400, + "src/main/updater.linux-root-package-install.test.ts": 1772, + "src/main/updater.nudge-campaign.test.ts": 1490, + "src/main/updater.prerelease-fallback.test.ts": 4396, + "src/main/updater.publishing-window-feed.test.ts": 3071, + "src/main/updater.quit-and-install.test.ts": 2199, + "src/main/updater.startup-scheduling.test.ts": 1573, + "src/main/usage-worktree-canonicalizer.test.ts": 10, + "src/main/usage-worktree-metadata.test.ts": 9, + "src/main/usage/highest-usage-key.test.ts": 14, + "src/main/usage/usage-breakdown-scaling.test.ts": 55, + "src/main/usage/usage-calendar-range.test.ts": 8, + "src/main/usage/usage-provider-store-lifecycle.test.ts": 293, + "src/main/usage/usage-worktree-refs.test.ts": 7, + "src/main/warp-themes/discovery.test.ts": 30, + "src/main/warp-themes/index.test.ts": 447, + "src/main/warp-themes/manual-warp-theme-files.test.ts": 36, + "src/main/warp-themes/parser-runner.test.ts": 9, + "src/main/warp-themes/parser.test.ts": 23, + "src/main/warp-themes/theme-file-scanner.test.ts": 22, + "src/main/win32-utils-async-acl.test.ts": 16, + "src/main/win32-utils.test.ts": 12, + "src/main/window/attach-main-window-services.test.ts": 195, + "src/main/window/clipboard-dashboard-popout-access.test.ts": 15, + "src/main/window/clipboard-file-copy.test.ts": 11, + "src/main/window/clipboard-image-temp-file.test.ts": 8, + "src/main/window/clipboard-image-thumbnail.test.ts": 8, + "src/main/window/clipboard-ipc-handlers.test.ts": 94, + "src/main/window/clipboard-remote-file-copy.test.ts": 18, + "src/main/window/clipboard-remote-file-staging-fs.test.ts": 23, + "src/main/window/clipboard-remote-file-staging.test.ts": 37, + "src/main/window/clipboard-runtime-owned-ssh-paste.test.ts": 15, + "src/main/window/clipboard-text-write-verify.test.ts": 8, + "src/main/window/clipboard-windows-image-file.test.ts": 17, + "src/main/window/createMainWindow-close-confirmation.test.ts": 29, + "src/main/window/createMainWindow-markdown-editor-focus.test.ts": 21, + "src/main/window/createMainWindow-recovery-reload-watchdog.test.ts": 48, + "src/main/window/createMainWindow-renderer-crash-recovery.test.ts": 51, + "src/main/window/createMainWindow-startup-reveal.test.ts": 35, + "src/main/window/createMainWindow-system-resume-relay.test.ts": 11, + "src/main/window/createMainWindow-terminal-focus-shortcuts.test.ts": 46, + "src/main/window/createMainWindow-tray-minimize-close.test.ts": 31, + "src/main/window/createMainWindow-zoom-and-tab-switch-shortcuts.test.ts": 17, + "src/main/window/createMainWindow.test.ts": 35, + "src/main/window/dashboard-popout-window.test.ts": 26, + "src/main/window/editable-context-menu.test.ts": 20, + "src/main/window/focus-existing-window.test.ts": 10, + "src/main/window/foreground-activation-policy.test.ts": 15, + "src/main/window/history-gc-profile-worktree-ids.test.ts": 13, + "src/main/window/history-gc-worktree-ids.test.ts": 8, + "src/main/window/macos-app-activation.test.ts": 5, + "src/main/window/macos-tahoe-release.test.ts": 6, + "src/main/window/main-window-visibility.test.ts": 5, + "src/main/window/main-window-webview-security.test.ts": 12, + "src/main/window/mobile-markdown-request-relay.test.ts": 26, + "src/main/window/privileged-window-navigation.test.ts": 14, + "src/main/window/renderer-document-navigation.test.ts": 9, + "src/main/window/renderer-publication-throttle.test.ts": 5, + "src/main/window/renderer-recovery-prompt.test.ts": 21, + "src/main/window/renderer-recovery-reload-watchdog.test.ts": 16, + "src/main/window/runtime-renderer-notification-sender.test.ts": 9, + "src/main/window/session-tab-close-request-relay.test.ts": 40, + "src/main/window/terminal-tab-close-request-relay.test.ts": 24, + "src/main/window/updater-package-recovery-ipc.test.ts": 14, + "src/main/window/window-close-decision.test.ts": 4, + "src/main/windows-descendant-exit-verification.test.ts": 17, + "src/main/windows-process-tree-kill.test.ts": 7, + "src/main/windows-pty-root-identity.test.ts": 13, + "src/main/windows/windows-command-line-recovery-health.test.ts": 10, + "src/main/windows/windows-process-table-cim-scan.test.ts": 6, + "src/main/windows/windows-process-table.test.ts": 64, + "src/main/windows/windows-process-tree-command-line-patch.test.ts": 11, + "src/main/windows/windows-pty-job.test.ts": 14, + "src/main/workspace-cleanup-removal-snapshot-prune.test.ts": 13, + "src/main/workspace-cleanup-scan-snapshot.test.ts": 88, + "src/main/workspace-space-analysis-capacity.test.ts": 38, + "src/main/workspace-space-analysis-du-timeout.test.ts": 236, + "src/main/workspace-space-analysis-snapshot.test.ts": 66, + "src/main/workspace-space-analysis.test.ts": 227, + "src/main/workspace-space-repo-scan.test.ts": 5, + "src/main/workspace-space-scan-control.test.ts": 36, + "src/main/worktree-create-base-prefetch.test.ts": 225, + "src/main/worktree-create-base.test.ts": 5, + "src/main/worktree-create-candidates.test.ts": 5, + "src/main/worktree-create-execution-host-route.test.ts": 10, + "src/main/worktree-create-preparation-cancellation.test.ts": 29, + "src/main/worktree-create-preparation-claim.test.ts": 8, + "src/main/worktree-create-preparation-wsl-root.test.ts": 13, + "src/main/worktree-create-preparation.test.ts": 110, + "src/main/worktree-create-timing.test.ts": 7, + "src/main/worktree-identity-persistence.test.ts": 207, + "src/main/worktree-lineage-pruning.test.ts": 12, + "src/main/worktree-metadata-ownership.test.ts": 4, + "src/main/worktree-name-retirement.test.ts": 18, + "src/main/worktree-removal-authority.test.ts": 10, + "src/main/worktree-removal-execution-host-route.test.ts": 6, + "src/main/worktree-removal-repo-owner.test.ts": 7, + "src/main/worktree-removal-safety.test.ts": 18, + "src/main/worktree-removal-session-partition-fencing.test.ts": 1223, + "src/main/worktree-retirement-backfill-scan.test.ts": 20, + "src/main/worktree-retirement-backfill-stall.test.ts": 15, + "src/main/worktree-retirement-discovery-wsl.test.ts": 32, + "src/main/worktree-retirement-discovery.test.ts": 37, + "src/main/worktree-retirement-namespace.test.ts": 28, + "src/main/worktree-root-preparation.test.ts": 11, + "src/main/worktree-trash.test.ts": 53, + "src/main/wsl-availability-missing-kernel.test.ts": 10, + "src/main/wsl-bash-command.test.ts": 4, + "src/main/wsl-distro-list-output.test.ts": 8, + "src/main/wsl-distro-list-single-flight.test.ts": 14, + "src/main/wsl-fish-history-cleanup.test.ts": 15, + "src/main/wsl-interop-spawn-directory.test.ts": 10, + "src/main/wsl-running-distros.test.ts": 125, + "src/main/wsl-unc-delete-symlink-repro.test.ts": 7, + "src/main/wsl-unc-delete.test.ts": 12, + "src/main/wsl.test.ts": 35, + "src/main/wsl/wsl-guest-environment.test.ts": 52, + "src/main/wsl/wsl-invocation-boundary.test.ts": 6, + "src/main/wsl/wsl-probe-failure-semantics.test.ts": 587, + "src/main/wsl/wsl-runner.test.ts": 32, + "src/main/wsl/wsl-w1-w3-contract.test.ts": 26, + "src/preload/api/platform-bridge.test.ts": 12, + "src/preload/app-restart-checkpoint-routing.test.ts": 651, + "src/preload/browser-client-page-renderer-requests.test.ts": 22, + "src/preload/browser-find-subscriptions.test.ts": 11, + "src/preload/browser-window-close.test.ts": 6, + "src/preload/close-active-tab-payload-admission.test.ts": 5, + "src/preload/doc-preview-link-interception.test.ts": 29, + "src/preload/pty-snapshot-capability-ipc.test.ts": 652, + "src/preload/renderer-heap-statistics-reader.test.ts": 5, + "src/preload/renderer-process-memory-reader.test.ts": 7, + "src/preload/renderer-restart-wiring.test.ts": 13, + "src/preload/runtime-environment-subscriptions.test.ts": 11, + "src/preload/ssh-authority-forwarding.test.ts": 682, + "src/preload/updater-package-recovery.test.ts": 273, + "src/preload/usage-provider-api.test.ts": 9, + "src/relay/agent-exec-handler-windows.test.ts": 11, + "src/relay/agent-exec-handler.test.ts": 28, + "src/relay/agent-hook-envelope-publication.test.ts": 215, + "src/relay/agent-hook-integration.test.ts": 116, + "src/relay/agent-hook-retired-pane-suppression.test.ts": 125, + "src/relay/agent-hook-server-codex-subagent-transcript.test.ts": 1068, + "src/relay/agent-hook-server.test.ts": 1481, + "src/relay/ai-vault-handler.test.ts": 208, + "src/relay/ai-vault-service-client.test.ts": 28, + "src/relay/ai-vault-service-restart-policy.test.ts": 10, + "src/relay/ai-vault-service-spawn.test.ts": 9, + "src/relay/context.test.ts": 5, + "src/relay/dispatcher-capacity-degradation.test.ts": 112, + "src/relay/dispatcher-client-close-cause.test.ts": 15, + "src/relay/dispatcher-client-writer.test.ts": 13, + "src/relay/dispatcher-frame-guard-regressions.test.ts": 7, + "src/relay/dispatcher-json-payload.test.ts": 51, + "src/relay/dispatcher-notification-ownership.test.ts": 5, + "src/relay/dispatcher-silent-client-reaper.test.ts": 19, + "src/relay/dispatcher-structured-error.test.ts": 10, + "src/relay/dispatcher-timeout.test.ts": 19, + "src/relay/dispatcher-writer-admission.test.ts": 94, + "src/relay/dispatcher.test.ts": 198, + "src/relay/external-automation-provider-catalog.test.ts": 27, + "src/relay/external-automations-handler-log-path.test.ts": 24, + "src/relay/external-automations-handler.test.ts": 21, + "src/relay/fs-handler-doc-preview.test.ts": 16, + "src/relay/fs-handler-file-range-dispatch.test.ts": 79, + "src/relay/fs-handler-file-range.test.ts": 61, + "src/relay/fs-handler-git-search.test.ts": 7, + "src/relay/fs-handler-install-rg.test.ts": 9, + "src/relay/fs-handler-list-files-cancel.test.ts": 28, + "src/relay/fs-handler-list-files-ignored.test.ts": 648, + "src/relay/fs-handler-list-files-result-limit.test.ts": 12, + "src/relay/fs-handler-readdir-fallback.test.ts": 8, + "src/relay/fs-handler-ripgrep-fallback.test.ts": 19, + "src/relay/fs-handler-stream.test.ts": 475, + "src/relay/fs-handler.test.ts": 172, + "src/relay/fs-list-files-cancel.integration.test.ts": 26, + "src/relay/fs-list-files-large-response.integration.test.ts": 87, + "src/relay/fs-list-files-scan-coordinator.test.ts": 14, + "src/relay/fs-path-metadata-requests.test.ts": 10, + "src/relay/fs-path-metadata-symlink-concurrency.test.ts": 11, + "src/relay/fs-search-line-fragments.test.ts": 84, + "src/relay/fs-stream-pty-echo-backpressure.integration.test.ts": 135, + "src/relay/git-branch-delete-refusal-parity.test.ts": 10, + "src/relay/git-exec-validator.test.ts": 18, + "src/relay/git-handler-blob-readers.test.ts": 9, + "src/relay/git-handler-branch-cleanup.test.ts": 19, + "src/relay/git-handler-branch-compare.test.ts": 58, + "src/relay/git-handler-branch-diff-equivalence.test.ts": 908, + "src/relay/git-handler-branch-diff.test.ts": 174, + "src/relay/git-handler-check-ignore.test.ts": 8, + "src/relay/git-handler-diff-read-coalescing.test.ts": 103, + "src/relay/git-handler-file-diff.test.ts": 1278, + "src/relay/git-handler-fork-remote-exec.test.ts": 75, + "src/relay/git-handler-pull-reconciliation.test.ts": 712, + "src/relay/git-handler-push-target.test.ts": 16, + "src/relay/git-handler-remote-sync.test.ts": 1241, + "src/relay/git-handler-staging.test.ts": 402, + "src/relay/git-handler-status-ops.test.ts": 63, + "src/relay/git-handler-submodule-cache-invalidation.test.ts": 8, + "src/relay/git-handler-submodule-ops.test.ts": 47, + "src/relay/git-handler-submodule-status-cancellation.test.ts": 11, + "src/relay/git-handler-termination.test.ts": 9, + "src/relay/git-handler-utils.test.ts": 4, + "src/relay/git-handler-working-tree-changes.test.ts": 1001, + "src/relay/git-handler-worktree-clean.test.ts": 9, + "src/relay/git-handler-worktree-git-capabilities.test.ts": 12, + "src/relay/git-handler-worktree-inspection.test.ts": 819, + "src/relay/git-handler-worktree-list-authority.test.ts": 12, + "src/relay/git-handler-worktree-list.test.ts": 11, + "src/relay/git-handler-worktree-ops.test.ts": 27, + "src/relay/git-handler-worktree-paths.test.ts": 5, + "src/relay/git-handler-worktree-provisioning.test.ts": 723, + "src/relay/git-handler.test.ts": 638, + "src/relay/git-porcelain-local-parity.test.ts": 21, + "src/relay/git-push-target-local-parity.test.ts": 21, + "src/relay/git-response-pty-echo-backpressure.integration.test.ts": 751, + "src/relay/git-response-stream-ownership.test.ts": 18, + "src/relay/git-status-branch-line-total.test.ts": 197, + "src/relay/git-status-upstream-negative-cache.test.ts": 260, + "src/relay/git-stdout-stream.test.ts": 21, + "src/relay/git-working-file-read.test.ts": 27, + "src/relay/hermes-run-correlation.test.ts": 8, + "src/relay/hermes-run-history.test.ts": 13, + "src/relay/integration.test.ts": 1222, + "src/relay/legacy-relay-publication-ledger.test.ts": 7, + "src/relay/managed-hook-installer.test.ts": 13, + "src/relay/node-pty-binding-survey.test.ts": 132, + "src/relay/node-pty-unavailable-diagnosis.test.ts": 13, + "src/relay/plugin-overlay-env.test.ts": 14, + "src/relay/plugin-overlay.test.ts": 24, + "src/relay/plugin-source-limit.test.ts": 8, + "src/relay/port-scan-handler.test.ts": 70, + "src/relay/preflight-handler.test.ts": 18, + "src/relay/protocol-backpressure.test.ts": 51, + "src/relay/protocol-handshake.test.ts": 7, + "src/relay/protocol-json-payload.test.ts": 178, + "src/relay/pty-handler-attach-replay.test.ts": 49, + "src/relay/pty-handler-dispose-lifecycle.test.ts": 37, + "src/relay/pty-handler-grace-timer.test.ts": 19, + "src/relay/pty-handler-inventory-process-evidence.test.ts": 30, + "src/relay/pty-handler-output-drain-differential.test.ts": 135, + "src/relay/pty-handler-output-streaming.test.ts": 420, + "src/relay/pty-handler-ownership-attestation.test.ts": 87, + "src/relay/pty-handler-resize-stale-pty.test.ts": 30, + "src/relay/pty-handler-retired-pane-surface.test.ts": 82, + "src/relay/pty-handler-revive.test.ts": 91, + "src/relay/pty-handler-shell-resolution.test.ts": 33, + "src/relay/pty-handler-shutdown-signals.test.ts": 54, + "src/relay/pty-handler-source-publication.test.ts": 260, + "src/relay/pty-handler-spawn-admission.test.ts": 127, + "src/relay/pty-handler-spawn-cwd.test.ts": 46, + "src/relay/pty-handler-spawn-environment.test.ts": 108, + "src/relay/pty-handler-startup-command-delivery.test.ts": 111, + "src/relay/pty-handler-windows-child-process-evidence.test.ts": 35, + "src/relay/pty-replay-buffer-equivalence.test.ts": 77, + "src/relay/pty-shell-launch.test.ts": 312, + "src/relay/pty-shell-utils.test.ts": 58, + "src/relay/pty-source-credit-ledger.test.ts": 122, + "src/relay/pty-source-credit-scheduler.test.ts": 12, + "src/relay/pty-source-sent-boundaries.test.ts": 88, + "src/relay/relay-command-env.test.ts": 14, + "src/relay/relay-daemon-fatal-reap.test.ts": 540, + "src/relay/relay-diagnostic-log.test.ts": 7, + "src/relay/relay-endpoint-credential-publication.test.ts": 2634, + "src/relay/relay-filesystem-watch-registry.test.ts": 128, + "src/relay/relay-grace-branch.test.ts": 7, + "src/relay/relay-handshake-roundtrip.test.ts": 317, + "src/relay/relay-launch-options.test.ts": 12, + "src/relay/relay-oversized-notification-survival.test.ts": 67, + "src/relay/relay-pty-consumer-owner-displacement.test.ts": 19, + "src/relay/relay-pty-publication-admission.test.ts": 26, + "src/relay/relay-pty-source-cancellation-exit.test.ts": 54, + "src/relay/relay-pty-source-exit-publication.test.ts": 27, + "src/relay/relay-pty-source-publication.test.ts": 69, + "src/relay/relay-pty-source-recovery-completion.test.ts": 9, + "src/relay/relay-pty-source-recovery-interleavings.test.ts": 25, + "src/relay/relay-pty-source-recovery-window.test.ts": 33, + "src/relay/relay-pty-source-restore-retry.test.ts": 15, + "src/relay/relay-pty-source-send-scheduler.test.ts": 9, + "src/relay/relay-pty-source-superseded-activation.test.ts": 21, + "src/relay/relay-reconnect-listener-credential-gate.test.ts": 28, + "src/relay/relay-watch-root-capacity.test.ts": 15, + "src/relay/relay-watcher-event-emitter.test.ts": 842, + "src/relay/relay-watcher-frame-chunking.test.ts": 632, + "src/relay/relay-watcher-parent-removal.test.ts": 20, + "src/relay/relay-watcher-pending-setup-waiters.test.ts": 233, + "src/relay/relay-watcher-setup-wait.test.ts": 320, + "src/relay/remote-artifact-cli-input.test.ts": 124, + "src/relay/remote-cli-env.test.ts": 5, + "src/relay/remote-cli-stdin.test.ts": 7, + "src/relay/remote-cli-timeout.test.ts": 9, + "src/relay/retired-pane-surfaces.test.ts": 7, + "src/relay/rotating-log-writer.test.ts": 33, + "src/relay/skill-install-handler.test.ts": 306, + "src/relay/skill-upload-multi-relay.integration.test.ts": 549, + "src/relay/ssh-pty-consumer-session-adapter.test.ts": 48, + "src/relay/ssh-pty-source-credit-adapter.test.ts": 29, + "src/relay/subprocess-tree-termination.test.ts": 6, + "src/relay/subprocess.test.ts": 22239, + "src/relay/terminal-history-wsl.test.ts": 11, + "src/relay/terminal-history.test.ts": 14, + "src/relay/windows-port-scan.test.ts": 66, + "src/relay/workspace-session-handler.test.ts": 12, + "src/relay/workspace-snapshot-publication.test.ts": 29, + "src/relay/workspace-space-scan-du-capacity.test.ts": 34, + "src/relay/workspace-space-scan.test.ts": 37, + "src/relay/wsl-agent-hook-relay.test.ts": 45, + "src/relay/wsl-hook-fs-bridge.test.ts": 11, + "src/relay/wsl-install-plugins-handler.test.ts": 20, + "src/renderer/src/app-shell/app-command-handlers-tab-rename.test.ts": 13, + "src/renderer/src/app-shell/app-command-handlers-workspace-delete.test.ts": 10, + "src/renderer/src/app-shell/app-root-surface-settings.test.tsx": 25, + "src/renderer/src/app-shell/reconcile-hydrated-workspace-tab-models.test.ts": 6, + "src/renderer/src/app-shell/remote-workspace-unplaced-upload-suppression.test.tsx": 92, + "src/renderer/src/app-shell/shutdown-checkpoint-persist.test.ts": 16, + "src/renderer/src/app-shell/shutdown-checkpoint-restart-lifecycle.test.ts": 20, + "src/renderer/src/app-shell/startup-actions-selector.test.ts": 56, + "src/renderer/src/app-shell/use-document-appearance.test.tsx": 33, + "src/renderer/src/app-shell/window-visibility-actions-selector.test.ts": 42, + "src/renderer/src/app-shell/workspace-view-cross-client-sync.test.tsx": 120, + "src/renderer/src/app-startup-routing.test.ts": 13, + "src/renderer/src/assets/mobile-page-qr-layout.test.ts": 5, + "src/renderer/src/assets/rich-markdown-task-list-style.test.ts": 4, + "src/renderer/src/assets/terminal-container-geometry.test.ts": 7, + "src/renderer/src/assets/terminal-scrollbar-style.test.ts": 4, + "src/renderer/src/assets/worktree-card-active-style.test.ts": 4, + "src/renderer/src/components/AgentStateDot.test.ts": 38, + "src/renderer/src/components/AgentWorkingSpinner.test.tsx": 43, + "src/renderer/src/components/LinuxPackageInstallRecoveryCard.test.tsx": 343, + "src/renderer/src/components/NewWorkspaceComposerCard.set-location-warm.test.tsx": 141, + "src/renderer/src/components/NewWorkspaceComposerCard.set-location.test.tsx": 229, + "src/renderer/src/components/NewWorkspaceComposerCard.test.tsx": 1044, + "src/renderer/src/components/QuickOpen.mount-gating.test.tsx": 647, + "src/renderer/src/components/SelectedTextCopyMenu.test.tsx": 49, + "src/renderer/src/components/StarNagCard.test.tsx": 60, + "src/renderer/src/components/StateIndicatorTooltip.test.tsx": 12, + "src/renderer/src/components/TerminalSearch.test.tsx": 106, + "src/renderer/src/components/TerminalTitlebarTabs.test.tsx": 28, + "src/renderer/src/components/TerminalWorkbenchContainer.test.tsx": 40, + "src/renderer/src/components/UpdateCard.error-card.test.tsx": 449, + "src/renderer/src/components/UpdateCard.test.ts": 60, + "src/renderer/src/components/WorktreeBaseFallbackDialog.test.tsx": 146, + "src/renderer/src/components/WorktreeJumpPalette.linear-url.test.tsx": 865, + "src/renderer/src/components/WorktreeJumpPalette.mount-gating.test.tsx": 489, + "src/renderer/src/components/WorktreeJumpPalette.recent-tabs.behavior.test.tsx": 466, + "src/renderer/src/components/WorktreeJumpPalette.recent-tabs.test.tsx": 907, + "src/renderer/src/components/WorktreeJumpPalette.test.tsx": 423, + "src/renderer/src/components/activity/ActivityPrototypePage.filter-focus-shortcut.test.ts": 8, + "src/renderer/src/components/activity/ActivityPrototypePage.test.ts": 21, + "src/renderer/src/components/activity/ActivityPrototypePage.thread-grouping.test.ts": 31, + "src/renderer/src/components/activity/ActivityThreadOptionsMenu.test.tsx": 388, + "src/renderer/src/components/activity/activity-auto-mark-read-loop.react185.test.tsx": 950, + "src/renderer/src/components/activity/activity-clear-completed.test.ts": 28, + "src/renderer/src/components/activity/activity-event-builder-agent-context.test.ts": 13, + "src/renderer/src/components/activity/activity-event-builder.bounded-history.test.ts": 12, + "src/renderer/src/components/activity/activity-event-builder.host-ownership.test.ts": 17, + "src/renderer/src/components/activity/activity-event-builder.identity-reuse.test.ts": 15, + "src/renderer/src/components/activity/activity-event-builder.live-cap.test.ts": 14, + "src/renderer/src/components/activity/activity-portal-churn-budget.test.ts": 8, + "src/renderer/src/components/activity/activity-portal-readiness-decay.react185.test.tsx": 33, + "src/renderer/src/components/activity/activity-portal-readiness-loop.react185.test.tsx": 144, + "src/renderer/src/components/activity/activity-portal-readiness-oscillation.test.ts": 5, + "src/renderer/src/components/activity/activity-portal-readiness-subscription-churn.react185.test.tsx": 131, + "src/renderer/src/components/activity/activity-portal-thread-reconciliation.test.ts": 9, + "src/renderer/src/components/activity/activity-scope-filter.test.ts": 9, + "src/renderer/src/components/activity/activity-terminal-portal-publication-loop.react185.test.tsx": 21, + "src/renderer/src/components/activity/activity-terminal-portal.test.tsx": 19, + "src/renderer/src/components/activity/activity-thread-actions.test.ts": 17, + "src/renderer/src/components/activity/activity-thread-child-agent.test.ts": 7, + "src/renderer/src/components/activity/activity-thread-grouping.search-cache.test.ts": 8, + "src/renderer/src/components/activity/activity-thread-grouping.status-order.test.ts": 17, + "src/renderer/src/components/activity/activity-thread-hover-card.test.tsx": 228, + "src/renderer/src/components/activity/activity-thread-list-pane-collapsible.test.tsx": 218, + "src/renderer/src/components/activity/activity-thread-list-pane.virtualization.test.tsx": 852, + "src/renderer/src/components/activity/activity-thread-presentation.test.ts": 9, + "src/renderer/src/components/activity/activity-thread-virtual-items.test.ts": 12, + "src/renderer/src/components/activity/dev-activity-fixture.test.ts": 7, + "src/renderer/src/components/activity/event-time-clock-refresh.test.tsx": 40, + "src/renderer/src/components/activity/use-activity-thread-action-bindings.test.tsx": 22, + "src/renderer/src/components/activity/useActivityUnreadCount.freshness.test.tsx": 42, + "src/renderer/src/components/activity/useActivityUnreadCount.test.ts": 7, + "src/renderer/src/components/agent-session-continuation/AgentSessionContinuationDialog.test.tsx": 42, + "src/renderer/src/components/agent-session-continuation/agent-session-continuation-selection.test.ts": 5, + "src/renderer/src/components/agent/AgentCombobox.test.tsx": 855, + "src/renderer/src/components/agent/AgentSettingsDialog.test.tsx": 68, + "src/renderer/src/components/agent/agent-combobox-command-state.test.ts": 6, + "src/renderer/src/components/artifacts/ArtifactCollection.test.tsx": 439, + "src/renderer/src/components/artifacts/ArtifactPreview.test.tsx": 69, + "src/renderer/src/components/artifacts/ArtifactPublishButton.test.tsx": 272, + "src/renderer/src/components/artifacts/ArtifactsPage.test.tsx": 1134, + "src/renderer/src/components/artifacts/artifact-display-labels.test.ts": 30, + "src/renderer/src/components/artifacts/artifact-list-search.test.ts": 11, + "src/renderer/src/components/artifacts/artifact-publish-flow.test.ts": 62, + "src/renderer/src/components/artifacts/artifact-published-link-client.test.ts": 13, + "src/renderer/src/components/automations/AutomationDestinationField.test.tsx": 285, + "src/renderer/src/components/automations/AutomationDetail.host.test.tsx": 104, + "src/renderer/src/components/automations/AutomationDetail.test.tsx": 81, + "src/renderer/src/components/automations/AutomationEditorPromptEditor.test.tsx": 35, + "src/renderer/src/components/automations/AutomationHostBadges.test.tsx": 191, + "src/renderer/src/components/automations/AutomationHostFilterNotice.test.tsx": 125, + "src/renderer/src/components/automations/AutomationListEmptyView.test.tsx": 53, + "src/renderer/src/components/automations/AutomationListHostGroups.test.tsx": 71, + "src/renderer/src/components/automations/AutomationListLocalRows.test.tsx": 298, + "src/renderer/src/components/automations/AutomationListSearchField.test.tsx": 74, + "src/renderer/src/components/automations/AutomationListTableHeader.test.tsx": 131, + "src/renderer/src/components/automations/AutomationOwnerConflictNotice.test.tsx": 60, + "src/renderer/src/components/automations/AutomationPromptDisclosure.test.tsx": 334, + "src/renderer/src/components/automations/AutomationRunHistory.test.tsx": 122, + "src/renderer/src/components/automations/AutomationRunsDashboardSurface.test.tsx": 23, + "src/renderer/src/components/automations/AutomationRunsTable.test.tsx": 60, + "src/renderer/src/components/automations/AutomationSchedulePicker.render.test.tsx": 915, + "src/renderer/src/components/automations/AutomationSchedulePicker.test.ts": 839, + "src/renderer/src/components/automations/AutomationTimeField.test.tsx": 248, + "src/renderer/src/components/automations/AutomationsDetailPane.run-count.test.tsx": 115, + "src/renderer/src/components/automations/AutomationsDetailPane.test.tsx": 178, + "src/renderer/src/components/automations/AutomationsListPanel.test.tsx": 271, + "src/renderer/src/components/automations/AutomationsPage.create-destination.test.tsx": 412, + "src/renderer/src/components/automations/AutomationsPage.cross-authority-actions.test.tsx": 137, + "src/renderer/src/components/automations/AutomationsPage.escape-precedence.test.tsx": 232, + "src/renderer/src/components/automations/AutomationsPage.external-scope.test.tsx": 212, + "src/renderer/src/components/automations/AutomationsPage.notice-recovery.test.tsx": 198, + "src/renderer/src/components/automations/AutomationsPage.refresh-selection.test.tsx": 376, + "src/renderer/src/components/automations/AutomationsPage.run-visibility.test.tsx": 133, + "src/renderer/src/components/automations/AutomationsPage.save-visibility.test.tsx": 195, + "src/renderer/src/components/automations/AutomationsPage.strict-mode-save-visibility.test.tsx": 233, + "src/renderer/src/components/automations/AutomationsPage.test.tsx": 644, + "src/renderer/src/components/automations/AutomationsPageBreadcrumb.test.tsx": 62, + "src/renderer/src/components/automations/CreateFromPicker.test.tsx": 75, + "src/renderer/src/components/automations/ExternalAutomationManagers.test.tsx": 247, + "src/renderer/src/components/automations/automation-authority-identity.test.ts": 8, + "src/renderer/src/components/automations/automation-captured-owner.test.ts": 8, + "src/renderer/src/components/automations/automation-create-destination.test.ts": 10, + "src/renderer/src/components/automations/automation-create-projects.test.ts": 6, + "src/renderer/src/components/automations/automation-detail-tab-navigation.test.ts": 16, + "src/renderer/src/components/automations/automation-editor-prompt-options.test.ts": 6, + "src/renderer/src/components/automations/automation-external-target-match.test.ts": 8, + "src/renderer/src/components/automations/automation-host-cache-controller.test.ts": 21, + "src/renderer/src/components/automations/automation-host-cache-health.test.ts": 9, + "src/renderer/src/components/automations/automation-host-cache.test.ts": 18, + "src/renderer/src/components/automations/automation-host-catalog-generation.test.ts": 14, + "src/renderer/src/components/automations/automation-host-catalog-order.test.ts": 19, + "src/renderer/src/components/automations/automation-host-catalog-source.test.ts": 11, + "src/renderer/src/components/automations/automation-host-catalog.test.ts": 64, + "src/renderer/src/components/automations/automation-host-client.test.ts": 7, + "src/renderer/src/components/automations/automation-host-detail-display.test.ts": 4, + "src/renderer/src/components/automations/automation-host-diagnostics.test.ts": 28, + "src/renderer/src/components/automations/automation-host-filter-resolution.test.ts": 28, + "src/renderer/src/components/automations/automation-host-health.test.ts": 7, + "src/renderer/src/components/automations/automation-host-invalidation-window-events.test.ts": 12, + "src/renderer/src/components/automations/automation-host-invalidation.test.ts": 17, + "src/renderer/src/components/automations/automation-host-list-rows.test.ts": 8, + "src/renderer/src/components/automations/automation-host-orphan-entry.test.ts": 22, + "src/renderer/src/components/automations/automation-host-picker-groups.test.ts": 19, + "src/renderer/src/components/automations/automation-host-recovery.test.ts": 5, + "src/renderer/src/components/automations/automation-host-scale-gates.test.ts": 195, + "src/renderer/src/components/automations/automation-host-scheduler.test.ts": 18, + "src/renderer/src/components/automations/automation-host-status-descriptors.test.ts": 24, + "src/renderer/src/components/automations/automation-list-empty-state.test.ts": 18, + "src/renderer/src/components/automations/automation-list-focus-recovery.test.ts": 7, + "src/renderer/src/components/automations/automation-list-keyboard-navigation.test.ts": 8, + "src/renderer/src/components/automations/automation-list-last-run.test.ts": 26, + "src/renderer/src/components/automations/automation-list-row-identity.test.ts": 7, + "src/renderer/src/components/automations/automation-list-search-rows.test.ts": 12, + "src/renderer/src/components/automations/automation-list-search.test.ts": 12, + "src/renderer/src/components/automations/automation-list-view-sort.test.ts": 175, + "src/renderer/src/components/automations/automation-list-view.test.ts": 15, + "src/renderer/src/components/automations/automation-project-groups.test.ts": 8, + "src/renderer/src/components/automations/automation-run-completion-evidence.test.ts": 7, + "src/renderer/src/components/automations/automation-run-context.test.ts": 5, + "src/renderer/src/components/automations/automation-run-history-keyboard-navigation.test.ts": 14, + "src/renderer/src/components/automations/automation-run-open-target.test.ts": 10, + "src/renderer/src/components/automations/automation-run-output-snapshot-equivalence.test.ts": 69, + "src/renderer/src/components/automations/automation-run-output-snapshot.test.ts": 47, + "src/renderer/src/components/automations/automation-run-view-state.test.ts": 11, + "src/renderer/src/components/automations/automation-runs-dashboard-model.test.ts": 6, + "src/renderer/src/components/automations/automation-scoped-list-client.test.ts": 76, + "src/renderer/src/components/automations/automation-setup-decision.test.ts": 8, + "src/renderer/src/components/automations/automation-source-display.test.ts": 10, + "src/renderer/src/components/automations/automation-target-availability.test.ts": 12, + "src/renderer/src/components/automations/automation-usage-model.test.ts": 6, + "src/renderer/src/components/automations/automation-write-invalidation.test.ts": 8, + "src/renderer/src/components/automations/external-automation-display.test.ts": 6, + "src/renderer/src/components/automations/external-automation-list-entries.test.ts": 7, + "src/renderer/src/components/automations/external-automation-run-table-state.test.ts": 9, + "src/renderer/src/components/automations/external-automation-scope-gating.test.ts": 11, + "src/renderer/src/components/automations/external-automation-scope-keys.test.ts": 7, + "src/renderer/src/components/automations/external-automation-source-availability.test.ts": 5, + "src/renderer/src/components/automations/hermes-cron-output-parse.test.ts": 27, + "src/renderer/src/components/automations/use-automation-host-catalog-readoption.test.tsx": 58, + "src/renderer/src/components/automations/use-automation-host-catalog.test.tsx": 56, + "src/renderer/src/components/automations/use-automation-list-focus-recovery.test.tsx": 26, + "src/renderer/src/components/automations/use-automation-list-search.test.tsx": 33, + "src/renderer/src/components/automations/use-automation-runs-dashboard.test.tsx": 53, + "src/renderer/src/components/automations/use-external-automation-scope-retention.test.tsx": 19, + "src/renderer/src/components/automations/use-selected-automation-run-history.test.tsx": 33, + "src/renderer/src/components/browser-cookie-import-google-disclosure.test.tsx": 99, + "src/renderer/src/components/browser-favicon.test.tsx": 55, + "src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.chrome-parity.test.tsx": 391, + "src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.dead-guest.test.tsx": 346, + "src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.download-notices.test.tsx": 295, + "src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.drag-focus.test.tsx": 551, + "src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.failure-remount.test.tsx": 274, + "src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.popup-notices.test.tsx": 111, + "src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.test.tsx": 370, + "src/renderer/src/components/browser-pane/ReopenBrowserPageOnServerButton.test.tsx": 102, + "src/renderer/src/components/browser-pane/annotate/BrowserAnnotationSendMenuContent.test.tsx": 10, + "src/renderer/src/components/browser-pane/annotate/GrabConfirmationSheet.test.ts": 7, + "src/renderer/src/components/browser-pane/annotate/browser-annotation-output.test.ts": 36, + "src/renderer/src/components/browser-pane/annotate/browser-page-annotation-tray.test.tsx": 326, + "src/renderer/src/components/browser-pane/annotate/markup-drawing-model.test.ts": 15, + "src/renderer/src/components/browser-pane/annotate/markup-screenshot-compose.test.ts": 8, + "src/renderer/src/components/browser-pane/annotate/markup-shape-render.test.ts": 7, + "src/renderer/src/components/browser-pane/annotate/use-markup-draw-hint.test.ts": 17, + "src/renderer/src/components/browser-pane/annotate/useGrabMode.test.ts": 369, + "src/renderer/src/components/browser-pane/annotate/useMarkupEditor.test.ts": 26, + "src/renderer/src/components/browser-pane/annotate/useMarkupPointerHandlers.test.ts": 18, + "src/renderer/src/components/browser-pane/assemble-chrome/BrowserAddressBar.test.tsx": 79, + "src/renderer/src/components/browser-pane/assemble-chrome/BrowserFind.test.tsx": 152, + "src/renderer/src/components/browser-pane/assemble-chrome/BrowserPane.remote-link-routing.test.ts": 6, + "src/renderer/src/components/browser-pane/assemble-chrome/BrowserPane.render-ipc.test.ts": 6, + "src/renderer/src/components/browser-pane/assemble-chrome/BrowserPane.webview-preferences.test.ts": 20, + "src/renderer/src/components/browser-pane/assemble-chrome/BrowserPaneOverlayLayer.test.tsx": 951, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-address-bar-expansion.test.ts": 7, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-address-bar-suggestions.test.ts": 23, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-deferred-lifecycle.test.tsx": 139, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-detected-browsers-summary.test.ts": 4, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-egress-indicator.test.tsx": 234, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-import-hint-visibility.test.ts": 4, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-navigation-control-row.test.tsx": 57, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-pane-page-selection.test.ts": 4, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.adoption-chrome.test.tsx": 1919, + "src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.retention-props.test.tsx": 93, + "src/renderer/src/components/browser-pane/assemble-chrome/context-menu-positioning.test.ts": 10, + "src/renderer/src/components/browser-pane/assemble-chrome/ssh-routed-browser-page-gate.test.tsx": 175, + "src/renderer/src/components/browser-pane/assemble-chrome/use-browser-page-chrome-focus.test.tsx": 118, + "src/renderer/src/components/browser-pane/browser-client-page-metadata-publisher.test.ts": 262, + "src/renderer/src/components/browser-pane/browser-client-page-metadata-route-census.test.ts": 6, + "src/renderer/src/components/browser-pane/browser-client-page-position-driver.test.ts": 47, + "src/renderer/src/components/browser-pane/browser-client-page-renderer-installation.test.ts": 12, + "src/renderer/src/components/browser-pane/browser-client-page-retained-drag-passthrough.test.ts": 57, + "src/renderer/src/components/browser-pane/browser-client-page-retained-registry.test.ts": 52, + "src/renderer/src/components/browser-pane/browser-download-destination-toast.test.ts": 7, + "src/renderer/src/components/browser-pane/browser-reopen-on-server.test.ts": 13, + "src/renderer/src/components/browser-pane/describe-page/browser-annotation-geometry.test.ts": 8, + "src/renderer/src/components/browser-pane/describe-page/browser-artifact-upload.test.ts": 11, + "src/renderer/src/components/browser-pane/describe-page/browser-favicon-url.test.ts": 7, + "src/renderer/src/components/browser-pane/describe-page/browser-overlay-shortcut-target.test.ts": 7, + "src/renderer/src/components/browser-pane/describe-page/browser-page-url-display.test.ts": 15, + "src/renderer/src/components/browser-pane/describe-page/live-browser-url-registry.test.ts": 6, + "src/renderer/src/components/browser-pane/host-guest/browser-automation-visibility.test.ts": 16, + "src/renderer/src/components/browser-pane/host-guest/browser-focus.test.ts": 9, + "src/renderer/src/components/browser-pane/host-guest/browser-guest-eviction-veto.test.ts": 10, + "src/renderer/src/components/browser-pane/host-guest/browser-guest-page-id-identity.test.ts": 6, + "src/renderer/src/components/browser-pane/host-guest/browser-guest-paint-retention.test.ts": 33, + "src/renderer/src/components/browser-pane/host-guest/browser-guest-retention-site-census.test.ts": 592, + "src/renderer/src/components/browser-pane/host-guest/browser-guest-worktree-retention.test.ts": 15, + "src/renderer/src/components/browser-pane/host-guest/browser-keyboard.test.ts": 20, + "src/renderer/src/components/browser-pane/host-guest/browser-page-evicted-guest-recovery.test.ts": 105, + "src/renderer/src/components/browser-pane/host-guest/browser-page-favicon-retention.test.ts": 12, + "src/renderer/src/components/browser-pane/host-guest/browser-page-guest-recovery.test.ts": 338, + "src/renderer/src/components/browser-pane/host-guest/browser-page-paintability.test.ts": 5, + "src/renderer/src/components/browser-pane/host-guest/browser-page-viewport.test.ts": 28, + "src/renderer/src/components/browser-pane/host-guest/browser-page-webview-surface.test.ts": 21, + "src/renderer/src/components/browser-pane/host-guest/browser-page-webview.test.ts": 5, + "src/renderer/src/components/browser-pane/host-guest/browser-page-zoom.test.ts": 16, + "src/renderer/src/components/browser-pane/host-guest/browser-system-resume.test.ts": 14, + "src/renderer/src/components/browser-pane/host-guest/browser-worktree-surface-paintability.test.ts": 6, + "src/renderer/src/components/browser-pane/host-guest/use-browser-page-slot-viewport.test.ts": 21, + "src/renderer/src/components/browser-pane/host-guest/use-browser-page-viewport-scroll-reporting.test.tsx": 35, + "src/renderer/src/components/browser-pane/host-guest/use-webview-drag-passthrough-active.test.tsx": 35, + "src/renderer/src/components/browser-pane/host-guest/webview-registry.test.ts": 44, + "src/renderer/src/components/browser-pane/navigate/browser-address-bar-navigation.test.ts": 12, + "src/renderer/src/components/browser-pane/navigate/browser-download-progress.test.ts": 7, + "src/renderer/src/components/browser-pane/navigate/browser-load-failure-overlay.test.tsx": 150, + "src/renderer/src/components/browser-pane/navigate/browser-notices.test.ts": 19, + "src/renderer/src/components/browser-pane/navigate/browser-page-download-activity.test.ts": 16, + "src/renderer/src/components/browser-pane/navigate/browser-reload-action.test.ts": 9, + "src/renderer/src/components/browser-pane/navigate/browser-webview-load-failure.test.ts": 11, + "src/renderer/src/components/browser-pane/navigate/chromium-error-page-poll-visibility.test.ts": 40, + "src/renderer/src/components/browser-pane/navigate/chromium-error-page-polling.test.ts": 5, + "src/renderer/src/components/browser-pane/navigate/use-browser-page-reload-actions.test.tsx": 19, + "src/renderer/src/components/browser-pane/restored-client-hosted-recovery-window.test.tsx": 308, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-frame-style.test.ts": 7, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-keyboard.test.ts": 4, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-input-model.test.ts": 8, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.address-bar.test.tsx": 293, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.chrome-chords.test.tsx": 434, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-errors.test.ts": 10, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-lifecycle.test.ts": 61, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-restart-scheduler.test.ts": 12, + "src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-status.test.ts": 6, + "src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-navigation.test.ts": 37, + "src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-stream-activation.test.ts": 54, + "src/renderer/src/components/browser-pane/workspace-doc/HtmlDocPreview.failure-message.test.tsx": 2857, + "src/renderer/src/components/browser-pane/workspace-doc/HtmlDocPreview.toolbar.test.tsx": 4032, + "src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.test.ts": 28, + "src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-identity.test.ts": 8, + "src/renderer/src/components/browser-pane/workspace-doc/doc-preview-external-link-confirmation.test.ts": 67, + "src/renderer/src/components/browser-pane/workspace-doc/doc-preview-webview-attach.test.ts": 14, + "src/renderer/src/components/browser-pane/workspace-doc/use-doc-preview-guest-tools.lazy-ref.test.tsx": 29, + "src/renderer/src/components/browser-pane/workspace-doc/use-doc-preview-guest-tools.test.ts": 29, + "src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.test.tsx": 41, + "src/renderer/src/components/browser-profile-user-agent-option.test.tsx": 82, + "src/renderer/src/components/browser-webauthn-account-dialog.test.tsx": 166, + "src/renderer/src/components/cmd-j/palette-activation-focus-routing.test.ts": 7, + "src/renderer/src/components/cmd-j/palette-duplicate-key-ghost-rows.test.tsx": 26, + "src/renderer/src/components/cmd-j/palette-filter-option-list.test.ts": 15, + "src/renderer/src/components/cmd-j/palette-filter-options.test.ts": 23, + "src/renderer/src/components/cmd-j/palette-filter.test.ts": 12, + "src/renderer/src/components/cmd-j/palette-focus-restore-target.test.ts": 11, + "src/renderer/src/components/cmd-j/palette-host-badge.test.ts": 15, + "src/renderer/src/components/cmd-j/palette-list-entry-render-keys.test.ts": 6, + "src/renderer/src/components/cmd-j/palette-live-status.test.tsx": 183, + "src/renderer/src/components/cmd-j/palette-query-tokens.test.ts": 9, + "src/renderer/src/components/cmd-j/palette-results.test.ts": 42, + "src/renderer/src/components/cmd-j/palette-section-render-cap.test.ts": 12, + "src/renderer/src/components/cmd-j/palette-session-age.test.ts": 16, + "src/renderer/src/components/cmd-j/plugin-quick-actions.test.ts": 9, + "src/renderer/src/components/cmd-j/quick-action-context.test.ts": 20, + "src/renderer/src/components/cmd-j/worktree-checks-review-index.test.ts": 27, + "src/renderer/src/components/cmd-j/worktree-palette-cache-inputs.test.ts": 5, + "src/renderer/src/components/codex-restart-chip.test.tsx": 120, + "src/renderer/src/components/codex-restart-notice-key.test.ts": 4, + "src/renderer/src/components/comment-code-context-state.test.ts": 5, + "src/renderer/src/components/comment-reply-target-state.test.ts": 6, + "src/renderer/src/components/confirmation-dialog-refresh-boundary.test.ts": 44, + "src/renderer/src/components/confirmation-dialog.test.tsx": 445, + "src/renderer/src/components/confirmation-skip-preference.test.ts": 109, + "src/renderer/src/components/contextual-tours/ContextualTourControl.test.ts": 8, + "src/renderer/src/components/contextual-tours/ContextualTourOverlay.remeasure.test.tsx": 313, + "src/renderer/src/components/contextual-tours/ContextualTourOverlay.test.tsx": 136, + "src/renderer/src/components/contextual-tours/ContextualTourOverlay.visibility.test.tsx": 2421, + "src/renderer/src/components/contextual-tours/ContextualTourOverlaySurface.localization.test.tsx": 191, + "src/renderer/src/components/contextual-tours/contextual-tour-floating-position.test.ts": 1879, + "src/renderer/src/components/contextual-tours/contextual-tour-gate.test.ts": 14, + "src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.test.ts": 91, + "src/renderer/src/components/contextual-tours/contextual-tour-step-actions.test.ts": 6, + "src/renderer/src/components/contextual-tours/request-contextual-tour-when-ready.test.ts": 15, + "src/renderer/src/components/contextual-tours/use-contextual-tour.test.ts": 8, + "src/renderer/src/components/contextual-tours/workspace-creation-tour-handoff.test.ts": 9, + "src/renderer/src/components/crash-report/CrashReportDialogSurface.overflow.test.tsx": 109, + "src/renderer/src/components/crash-report/crash-report-submit-notice.test.ts": 13, + "src/renderer/src/components/crash-report/use-crash-report-copy.test.tsx": 21, + "src/renderer/src/components/dashboard-popout/AgentKanbanBoard.test.tsx": 469, + "src/renderer/src/components/dashboard-popout/AgentKanbanCard.test.tsx": 393, + "src/renderer/src/components/dashboard-popout/AgentMap.test.tsx": 1741, + "src/renderer/src/components/dashboard-popout/AgentMapCanvas.performance.test.tsx": 75, + "src/renderer/src/components/dashboard-popout/AgentMapFilterPanel.test.tsx": 126, + "src/renderer/src/components/dashboard-popout/AgentMapMotion.test.tsx": 189, + "src/renderer/src/components/dashboard-popout/AgentMapProjectLabel.test.tsx": 106, + "src/renderer/src/components/dashboard-popout/AgentMapRingHover.test.tsx": 289, + "src/renderer/src/components/dashboard-popout/AgentMapStatusGlow.test.tsx": 732, + "src/renderer/src/components/dashboard-popout/AgentMapTimeRangeField.test.tsx": 1678, + "src/renderer/src/components/dashboard-popout/AgentMapWorkspaceContextMenu.boundary.test.ts": 7, + "src/renderer/src/components/dashboard-popout/AgentMapWorkspaceContextMenu.test.tsx": 2066, + "src/renderer/src/components/dashboard-popout/AgentTerminalDialog.test.tsx": 234, + "src/renderer/src/components/dashboard-popout/AgentTerminalPreview.clipboard-routes.test.tsx": 1314, + "src/renderer/src/components/dashboard-popout/AgentTerminalPreview.option-dead-key.test.tsx": 149, + "src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx": 1273, + "src/renderer/src/components/dashboard-popout/DashboardHostBadge.test.tsx": 64, + "src/renderer/src/components/dashboard-popout/DashboardPopoutRoot.test.tsx": 26, + "src/renderer/src/components/dashboard-popout/agent-board-filtering.test.ts": 8, + "src/renderer/src/components/dashboard-popout/agent-map-filter.test.ts": 9, + "src/renderer/src/components/dashboard-popout/agent-map-glow.performance.test.ts": 12, + "src/renderer/src/components/dashboard-popout/agent-map-hover-containment.test.ts": 7, + "src/renderer/src/components/dashboard-popout/agent-map-label-declutter.test.ts": 12, + "src/renderer/src/components/dashboard-popout/agent-map-layout.test.ts": 152, + "src/renderer/src/components/dashboard-popout/agent-map-lineage-chevron-path.test.ts": 26, + "src/renderer/src/components/dashboard-popout/agent-map-navigation.test.ts": 9, + "src/renderer/src/components/dashboard-popout/agent-map-node-metadata.test.ts": 10, + "src/renderer/src/components/dashboard-popout/agent-map-node-presentation.test.ts": 4, + "src/renderer/src/components/dashboard-popout/agent-map-quick-views.test.ts": 11, + "src/renderer/src/components/dashboard-popout/agent-map-time-filter.test.ts": 6, + "src/renderer/src/components/dashboard-popout/agent-map-workspace-visibility.test.ts": 5, + "src/renderer/src/components/dashboard-popout/agent-map-worktree-active-status.test.ts": 8, + "src/renderer/src/components/dashboard-popout/agent-map-worktree-lineage-layout.test.ts": 161, + "src/renderer/src/components/dashboard-popout/agent-map-worktree-packing.test.ts": 1600, + "src/renderer/src/components/dashboard-popout/dashboard-agent-status-patch.test.ts": 9, + "src/renderer/src/components/dashboard-popout/preview-grid-claim.test.ts": 18, + "src/renderer/src/components/dashboard-popout/preview-terminal-ime-bridge-kitty-bytes.test.ts": 228, + "src/renderer/src/components/dashboard-popout/preview-terminal-options.test.ts": 11, + "src/renderer/src/components/dashboard-popout/preview-terminal-right-click-paste.test.ts": 63, + "src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.test.ts": 13, + "src/renderer/src/components/dashboard-popout/preview-terminal-snapshot-replay.test.ts": 11, + "src/renderer/src/components/dashboard-popout/terminal-preview-unavailable-message.test.ts": 10, + "src/renderer/src/components/dashboard-popout/useAgentMapFilters.test.tsx": 36, + "src/renderer/src/components/dashboard-popout/useDashboardSnapshot.test.tsx": 59, + "src/renderer/src/components/dashboard/AgentDashboardDrawer.test.tsx": 227, + "src/renderer/src/components/dashboard/AgentDashboardSettingsMenu.test.tsx": 68, + "src/renderer/src/components/dashboard/DashboardAgentRow.test.tsx": 97, + "src/renderer/src/components/dashboard/agent-dashboard-performance-isolation.test.ts": 6, + "src/renderer/src/components/dashboard/agent-finished-timestamp.test.ts": 5, + "src/renderer/src/components/dashboard/agent-row-lineage-model.test.ts": 6, + "src/renderer/src/components/dashboard/agent-row-pane-live-title.test.ts": 7, + "src/renderer/src/components/dashboard/build-dashboard-bucket-counts.allocation.test.ts": 97, + "src/renderer/src/components/dashboard/build-dashboard-bucket-counts.cache.test.ts": 11, + "src/renderer/src/components/dashboard/build-dashboard-bucket-counts.equivalence.test.ts": 40, + "src/renderer/src/components/dashboard/build-dashboard-snapshot-folder-workspace.test.ts": 21, + "src/renderer/src/components/dashboard/build-dashboard-snapshot-orchestration-routing.test.ts": 15, + "src/renderer/src/components/dashboard/build-dashboard-snapshot.rows-cache.test.ts": 30, + "src/renderer/src/components/dashboard/build-dashboard-snapshot.test.ts": 34, + "src/renderer/src/components/dashboard/dashboard-card-context.test.ts": 14, + "src/renderer/src/components/dashboard/dashboard-card-labels.test.ts": 5, + "src/renderer/src/components/dashboard/dashboard-card-terminal-input.test.ts": 11, + "src/renderer/src/components/dashboard/dashboard-worktree-launch-options.test.ts": 19, + "src/renderer/src/components/dashboard/launch-dashboard-agent.test.ts": 7, + "src/renderer/src/components/dashboard/use-agent-row-conversation-name.test.ts": 10, + "src/renderer/src/components/dashboard/useAgentBucketCounts.gate.test.ts": 46, + "src/renderer/src/components/dashboard/useAgentBucketCounts.test.tsx": 35, + "src/renderer/src/components/dashboard/useDashboardPopoutBridge.test.tsx": 57, + "src/renderer/src/components/dashboard/useLiveDashboardSnapshot.test.ts": 63, + "src/renderer/src/components/dashboard/useRetainedAgents.test.ts": 66, + "src/renderer/src/components/dashboard/useRetainedAgentsSync.test.ts": 6, + "src/renderer/src/components/dictation/DictationIndicator.localization.test.ts": 7, + "src/renderer/src/components/dictation/DictationIndicator.test.tsx": 86, + "src/renderer/src/components/dictation/dictation-audio-meter.test.ts": 5, + "src/renderer/src/components/dictation/dictation-final-segments.test.ts": 5, + "src/renderer/src/components/dictation/dictation-insertion-target.test.ts": 288, + "src/renderer/src/components/dictation/dictation-meter-store.test.tsx": 15, + "src/renderer/src/components/dictation/dictation-stopped-sessions.test.ts": 9, + "src/renderer/src/components/dictation/microphone-devices.test.ts": 16, + "src/renderer/src/components/dictation/use-hold-dictation-gesture.test.tsx": 43, + "src/renderer/src/components/diff-comments/DiffCommentCard.resize.test.tsx": 127, + "src/renderer/src/components/diff-comments/diff-comment-popover-outside-click.test.tsx": 59, + "src/renderer/src/components/diff-comments/diff-comment-popover-position.test.ts": 8, + "src/renderer/src/components/diff-comments/diff-comment-zone-mouse-events.test.ts": 5, + "src/renderer/src/components/diff-comments/useDiffCommentDecorator.commentable-lines.test.tsx": 43, + "src/renderer/src/components/diff-comments/useDiffCommentDecorator.model-lifecycle.test.tsx": 28, + "src/renderer/src/components/editor/ChangesModeView.test.tsx": 27, + "src/renderer/src/components/editor/CheckRunCopyButton.test.tsx": 70, + "src/renderer/src/components/editor/CheckRunDetailsPanel.copy.test.tsx": 116, + "src/renderer/src/components/editor/ConflictComponents.test.tsx": 4, + "src/renderer/src/components/editor/EditorContent.markdown-classification.test.tsx": 60, + "src/renderer/src/components/editor/EditorContent.monaco-lifecycle.test.tsx": 49, + "src/renderer/src/components/editor/EditorContent.test.tsx": 37, + "src/renderer/src/components/editor/EditorPanel.markdown-classification-memoization.test.tsx": 462, + "src/renderer/src/components/editor/EditorPanelHeader.test.tsx": 34, + "src/renderer/src/components/editor/EditorPanelMarkdownActionsMenu.test.tsx": 21, + "src/renderer/src/components/editor/EditorPanelShell.header.test.tsx": 18, + "src/renderer/src/components/editor/ExternalFileChangeBanner.test.tsx": 56, + "src/renderer/src/components/editor/ExternalFileChangeCompareDialog.test.tsx": 186, + "src/renderer/src/components/editor/ImageViewer.test.tsx": 105, + "src/renderer/src/components/editor/LargeDiffLoadPrompt.test.tsx": 42, + "src/renderer/src/components/editor/MarkdownPreview.link-routing.interaction.test.tsx": 106, + "src/renderer/src/components/editor/MarkdownPreview.test.ts": 9, + "src/renderer/src/components/editor/MarkdownPreview.toc-visibility-gate.test.tsx": 107, + "src/renderer/src/components/editor/MarkdownTableOfContentsPanel.test.tsx": 23, + "src/renderer/src/components/editor/MonacoEditor.content-owner.test.tsx": 56, + "src/renderer/src/components/editor/MonacoEditor.font-family.test.tsx": 46, + "src/renderer/src/components/editor/NotesSendMenu.test.tsx": 34, + "src/renderer/src/components/editor/ReviewNotesSendMenuContent.test.tsx": 30, + "src/renderer/src/components/editor/RichMarkdownErrorBoundary.lazy-chunk.test.tsx": 104, + "src/renderer/src/components/editor/RichMarkdownLinkBubble.render.test.tsx": 106, + "src/renderer/src/components/editor/RichMarkdownLinkBubble.test.ts": 3, + "src/renderer/src/components/editor/RichMarkdownSearchBar.ime-enter.test.tsx": 187, + "src/renderer/src/components/editor/RichMarkdownSlashMenu.test.tsx": 14, + "src/renderer/src/components/editor/RichMarkdownTableControls.test.tsx": 272, + "src/renderer/src/components/editor/check-annotation-open.test.ts": 9, + "src/renderer/src/components/editor/check-annotation-path.test.ts": 10, + "src/renderer/src/components/editor/check-job-step-status.test.ts": 5, + "src/renderer/src/components/editor/check-run-clipboard-text.test.ts": 8, + "src/renderer/src/components/editor/check-run-details-fix-with-ai.test.ts": 16, + "src/renderer/src/components/editor/check-run-details-tab.test.ts": 7, + "src/renderer/src/components/editor/closed-editor-tab-cache-sweep.test.ts": 10, + "src/renderer/src/components/editor/closed-editor-tab-disposal.test.ts": 48, + "src/renderer/src/components/editor/combined-diff-on-demand-load.test.ts": 9, + "src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-collapsed-work.test.tsx": 96, + "src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-render.test.tsx": 241, + "src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-windowing.test.tsx": 212, + "src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree.test.ts": 13, + "src/renderer/src/components/editor/combined-diff/browse-files/use-combined-diff-file-tree-resize.test.tsx": 44, + "src/renderer/src/components/editor/combined-diff/browse-files/use-combined-diff-tree-navigation.test.ts": 17, + "src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-initial-section-load.test.ts": 5, + "src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-load-scheduler.test.ts": 7, + "src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-connection.test.ts": 11, + "src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-state.test.ts": 10, + "src/renderer/src/components/editor/combined-diff/remember-view/use-combined-diff-view-restore.test.tsx": 29, + "src/renderer/src/components/editor/combined-diff/resolve-changes/combined-diff-entries.test.ts": 12, + "src/renderer/src/components/editor/combined-diff/resolve-changes/combined-diff-section-cache-match.test.ts": 5, + "src/renderer/src/components/editor/combined-diff/resolve-changes/combined-diff-section-scaling.test.tsx": 157, + "src/renderer/src/components/editor/combined-diff/resolve-changes/use-combined-diff-section-index-map.test.tsx": 23, + "src/renderer/src/components/editor/combined-diff/review-controls/combined-diff-commit-message.test.ts": 17, + "src/renderer/src/components/editor/combined-diff/scroll-viewport/combined-diff-restore-signal-equivalence.test.tsx": 291, + "src/renderer/src/components/editor/combined-diff/scroll-viewport/combined-diff-scrollbar-drag.test.ts": 8, + "src/renderer/src/components/editor/csv-parse.test.ts": 38, + "src/renderer/src/components/editor/details-markdown-html.test.ts": 24, + "src/renderer/src/components/editor/diff-editor-line-number-options.test.ts": 8, + "src/renderer/src/components/editor/diff-editor-shift-wheel-scroll.test.ts": 14, + "src/renderer/src/components/editor/diff-editor-whitespace-options.test.ts": 6, + "src/renderer/src/components/editor/diff-editor-word-wrap-options.test.ts": 4, + "src/renderer/src/components/editor/diff-line-stats.test.ts": 10, + "src/renderer/src/components/editor/diff-model-swap-view-state.test.ts": 8, + "src/renderer/src/components/editor/diff-monaco-model-disposal.test.ts": 12, + "src/renderer/src/components/editor/diff-navigation-context.test.tsx": 45, + "src/renderer/src/components/editor/diff-section-layout.test.ts": 30, + "src/renderer/src/components/editor/diff-section-preview.test.ts": 9, + "src/renderer/src/components/editor/diff-viewer-large-diff-save-action.test.ts": 7, + "src/renderer/src/components/editor/editor-autosave-conflict-flow.test.ts": 37, + "src/renderer/src/components/editor/editor-autosave-controller.test.ts": 40, + "src/renderer/src/components/editor/editor-autosave.test.ts": 17, + "src/renderer/src/components/editor/editor-cmd-save-target.test.ts": 10, + "src/renderer/src/components/editor/editor-content-dirty-state.test.ts": 21, + "src/renderer/src/components/editor/editor-external-watch-path-index.test.ts": 9, + "src/renderer/src/components/editor/editor-file-save-attempt.test.ts": 11, + "src/renderer/src/components/editor/editor-header.test.ts": 8, + "src/renderer/src/components/editor/editor-labels.test.ts": 6, + "src/renderer/src/components/editor/editor-panel-diff-reload.test.ts": 8, + "src/renderer/src/components/editor/editor-panel-draft-selector.test.ts": 20, + "src/renderer/src/components/editor/editor-panel-git-entry-selector.test.ts": 24, + "src/renderer/src/components/editor/editor-panel-render-model.test.ts": 29, + "src/renderer/src/components/editor/editor-path-move-inflight.test.ts": 9, + "src/renderer/src/components/editor/editor-restored-tab-conflict-scan.test.ts": 38, + "src/renderer/src/components/editor/editor-self-write-registry.test.ts": 20, + "src/renderer/src/components/editor/editor-shortcuts.test.ts": 23, + "src/renderer/src/components/editor/export-active-markdown.test.ts": 7, + "src/renderer/src/components/editor/file-editor-word-wrap-options.test.ts": 4, + "src/renderer/src/components/editor/image-viewer-zoom.test.ts": 10, + "src/renderer/src/components/editor/ipynb-code-cell-lines.test.ts": 138, + "src/renderer/src/components/editor/ipynb-parse.test.ts": 26, + "src/renderer/src/components/editor/large-diff-render-limit.test.ts": 103, + "src/renderer/src/components/editor/line-copy-path.test.ts": 5, + "src/renderer/src/components/editor/local-log-tail-decoder.test.ts": 8, + "src/renderer/src/components/editor/markdown-artifact-upload.test.ts": 7, + "src/renderer/src/components/editor/markdown-dirty-state.test.ts": 154, + "src/renderer/src/components/editor/markdown-doc-completions.test.ts": 11, + "src/renderer/src/components/editor/markdown-doc-links.test.ts": 29, + "src/renderer/src/components/editor/markdown-document-list-request.test.ts": 14, + "src/renderer/src/components/editor/markdown-document-worktree-path-selector.test.ts": 758, + "src/renderer/src/components/editor/markdown-export-extract.test.ts": 21, + "src/renderer/src/components/editor/markdown-export-html.test.ts": 5, + "src/renderer/src/components/editor/markdown-frontmatter.test.ts": 7, + "src/renderer/src/components/editor/markdown-heading-slug.test.ts": 6, + "src/renderer/src/components/editor/markdown-internal-links.test.ts": 10, + "src/renderer/src/components/editor/markdown-preview-annotation-shortcut.test.ts": 25, + "src/renderer/src/components/editor/markdown-preview-controls.test.ts": 10, + "src/renderer/src/components/editor/markdown-preview-links.test.ts": 8, + "src/renderer/src/components/editor/markdown-preview-local-images.test.ts": 67, + "src/renderer/src/components/editor/markdown-preview-search-crash.test.tsx": 24, + "src/renderer/src/components/editor/markdown-preview-search.test.ts": 16, + "src/renderer/src/components/editor/markdown-preview-url-transform.test.ts": 14, + "src/renderer/src/components/editor/markdown-reference-link-normalization.test.ts": 25, + "src/renderer/src/components/editor/markdown-render-mode.test.ts": 5, + "src/renderer/src/components/editor/markdown-rich-mode-eligibility-cache.test.ts": 225, + "src/renderer/src/components/editor/markdown-rich-mode.test.ts": 276, + "src/renderer/src/components/editor/markdown-rich-size-limit.test.ts": 7, + "src/renderer/src/components/editor/markdown-round-trip.test.ts": 388, + "src/renderer/src/components/editor/markdown-table-of-contents.test.ts": 57, + "src/renderer/src/components/editor/markdown-toc-collapse-state.test.ts": 5, + "src/renderer/src/components/editor/markdown-toc-visibility-gate.test.ts": 18, + "src/renderer/src/components/editor/mermaid-config.test.ts": 6, + "src/renderer/src/components/editor/monaco-auto-height.test.ts": 42, + "src/renderer/src/components/editor/monaco-codebase-search.test.ts": 8, + "src/renderer/src/components/editor/monaco-conflict-decorations.test.ts": 12, + "src/renderer/src/components/editor/monaco-content-sync.test.ts": 14, + "src/renderer/src/components/editor/monaco-content-sync.undo-history.test.ts": 44, + "src/renderer/src/components/editor/monaco-context-menu-paste.test.ts": 59, + "src/renderer/src/components/editor/monaco-find-options.test.ts": 5, + "src/renderer/src/components/editor/monaco-find-widget.test.ts": 12, + "src/renderer/src/components/editor/monaco-large-text-paste.test.ts": 19, + "src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.offset-scan.test.ts": 72, + "src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.test.ts": 15, + "src/renderer/src/components/editor/monaco-markdown-selection-annotation.test.ts": 9, + "src/renderer/src/components/editor/monaco-programmatic-sync.test.ts": 5, + "src/renderer/src/components/editor/monaco-reveal-range.test.ts": 4, + "src/renderer/src/components/editor/monaco-view-state-persistence.test.ts": 5, + "src/renderer/src/components/editor/pdf-scale-preference.test.ts": 8, + "src/renderer/src/components/editor/pdf-view-position.test.ts": 19, + "src/renderer/src/components/editor/pending-editor-focus-request.test.ts": 6, + "src/renderer/src/components/editor/position-stable-node-view-update.test.ts": 7, + "src/renderer/src/components/editor/restored-editor-owner-save-lifecycle.test.ts": 321, + "src/renderer/src/components/editor/rich-markdown-auto-focus.test.ts": 12, + "src/renderer/src/components/editor/rich-markdown-code-block-languages.test.ts": 8, + "src/renderer/src/components/editor/rich-markdown-commands.test.ts": 95, + "src/renderer/src/components/editor/rich-markdown-context-command-routing.test.ts": 56, + "src/renderer/src/components/editor/rich-markdown-cut.test.ts": 142, + "src/renderer/src/components/editor/rich-markdown-details-keyboard.test.ts": 83, + "src/renderer/src/components/editor/rich-markdown-doc-link.test.ts": 7, + "src/renderer/src/components/editor/rich-markdown-editor-click-routing.test.ts": 13, + "src/renderer/src/components/editor/rich-markdown-editor-config.test.ts": 15, + "src/renderer/src/components/editor/rich-markdown-empty-paragraph-delete.test.ts": 57, + "src/renderer/src/components/editor/rich-markdown-html-superscript-link.test.ts": 581, + "src/renderer/src/components/editor/rich-markdown-image-insert-code-block.test.ts": 270, + "src/renderer/src/components/editor/rich-markdown-image-insert.test.ts": 54, + "src/renderer/src/components/editor/rich-markdown-inline-image-paragraph.test.ts": 90, + "src/renderer/src/components/editor/rich-markdown-key-handler.test.ts": 83, + "src/renderer/src/components/editor/rich-markdown-large-text-paste.test.ts": 17, + "src/renderer/src/components/editor/rich-markdown-link-clipboard.test.ts": 7, + "src/renderer/src/components/editor/rich-markdown-link-shortcut.test.ts": 96, + "src/renderer/src/components/editor/rich-markdown-list-continuation.test.ts": 35, + "src/renderer/src/components/editor/rich-markdown-list-tokenizers.test.ts": 11, + "src/renderer/src/components/editor/rich-markdown-local-image.test.ts": 128, + "src/renderer/src/components/editor/rich-markdown-lowlight-cache.test.ts": 80, + "src/renderer/src/components/editor/rich-markdown-lowlight.test.ts": 2575, + "src/renderer/src/components/editor/rich-markdown-normalize.test.ts": 43, + "src/renderer/src/components/editor/rich-markdown-paragraph.test.ts": 9, + "src/renderer/src/components/editor/rich-markdown-paste-handler.test.ts": 8, + "src/renderer/src/components/editor/rich-markdown-paste-image.test.ts": 11, + "src/renderer/src/components/editor/rich-markdown-range-bounds.test.ts": 66, + "src/renderer/src/components/editor/rich-markdown-review-annotations.test.ts": 15, + "src/renderer/src/components/editor/rich-markdown-review-note-layout.test.ts": 6, + "src/renderer/src/components/editor/rich-markdown-review-rail-blocks.test.ts": 203, + "src/renderer/src/components/editor/rich-markdown-review-text-ranges.test.ts": 26, + "src/renderer/src/components/editor/rich-markdown-search-matches-cache.test.ts": 38, + "src/renderer/src/components/editor/rich-markdown-search.test.ts": 10, + "src/renderer/src/components/editor/rich-markdown-serialization-commit.test.ts": 21, + "src/renderer/src/components/editor/rich-markdown-slash-command-filter.test.ts": 7, + "src/renderer/src/components/editor/rich-markdown-source-reconcile.test.ts": 332, + "src/renderer/src/components/editor/rich-markdown-tab-key-handler.test.ts": 130, + "src/renderer/src/components/editor/rich-markdown-table-actions.test.ts": 475, + "src/renderer/src/components/editor/rich-markdown-table-control-layout.test.ts": 4, + "src/renderer/src/components/editor/rich-markdown-table-keyboard.test.ts": 105, + "src/renderer/src/components/editor/rich-markdown-terminal-path-paste.test.ts": 11, + "src/renderer/src/components/editor/rich-markdown-toc-heading-target.test.ts": 11, + "src/renderer/src/components/editor/selection-copy.test.ts": 8, + "src/renderer/src/components/editor/setup-contextual-copy.test.ts": 10, + "src/renderer/src/components/editor/untitled-file-rename-path.test.ts": 6, + "src/renderer/src/components/editor/use-monaco-editor-decorations.doc-link-refresh.test.tsx": 42, + "src/renderer/src/components/editor/use-rich-markdown-table-context-menu.test.tsx": 86, + "src/renderer/src/components/editor/use-rich-markdown-table-control-target.test.tsx": 83, + "src/renderer/src/components/editor/useDiffViewerLargeDiffLifecycle.test.tsx": 16, + "src/renderer/src/components/editor/useEditorCmdSaveRequest.test.tsx": 34, + "src/renderer/src/components/editor/useEditorPanelContentState.test.tsx": 823, + "src/renderer/src/components/editor/useEditorPanelExternalContentEvents.test.tsx": 32, + "src/renderer/src/components/editor/useEditorPanelFileLoadRetry.test.tsx": 53, + "src/renderer/src/components/editor/useEditorPanelRemoteSiblingContentState.test.tsx": 25, + "src/renderer/src/components/editor/useEditorPanelVisibilityContentState.test.tsx": 151, + "src/renderer/src/components/editor/useIpynbCellExecution.test.tsx": 78, + "src/renderer/src/components/editor/useIpynbDocumentEditing.test.tsx": 29, + "src/renderer/src/components/editor/useLinkBubble.test.tsx": 37, + "src/renderer/src/components/editor/useLocalImageSrc.test.ts": 74, + "src/renderer/src/components/editor/useLocalLogTail.test.tsx": 234, + "src/renderer/src/components/editor/useMarkdownDocuments.test.ts": 9, + "src/renderer/src/components/editor/useRichMarkdownEditorInstance.integration.test.tsx": 71, + "src/renderer/src/components/editor/useRichMarkdownEditorInstance.test.tsx": 23, + "src/renderer/src/components/editor/useRichMarkdownPendingFocus.test.ts": 20, + "src/renderer/src/components/editor/useRichMarkdownProgrammaticSync.test.ts": 77, + "src/renderer/src/components/editor/useRichMarkdownReviewController.open-guard.test.ts": 17, + "src/renderer/src/components/editor/useRichMarkdownSearch.reuse.test.tsx": 55, + "src/renderer/src/components/editor/useRichMarkdownSearch.test.tsx": 31, + "src/renderer/src/components/emulator-pane/MobileEmulatorTabIntroCallout.test.tsx": 60, + "src/renderer/src/components/emulator-pane/emulator-device-frame-layout.test.ts": 10, + "src/renderer/src/components/emulator-pane/emulator-device-frame-visibility.test.tsx": 158, + "src/renderer/src/components/emulator-pane/emulator-device-frame.input.test.tsx": 188, + "src/renderer/src/components/emulator-pane/emulator-device-state.test.ts": 5, + "src/renderer/src/components/emulator-pane/emulator-keyboard-paste.test.ts": 16, + "src/renderer/src/components/emulator-pane/emulator-screen-gesture.test.ts": 11, + "src/renderer/src/components/emulator-pane/emulator-screen-stream-content.test.tsx": 43, + "src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-cli-state.test.ts": 6, + "src/renderer/src/components/emulator-pane/mobile-emulator-agent-setup-visibility.test.ts": 4, + "src/renderer/src/components/emulator-pane/mobile-emulator-hidden-toast.test.tsx": 10, + "src/renderer/src/components/emulator-pane/mobile-emulator-tab-intro-visibility.test.ts": 7, + "src/renderer/src/components/emulator-pane/use-emulator-pane-session.test.tsx": 53, + "src/renderer/src/components/emulator-pane/use-mobile-emulator-tab-intro-actions.test.tsx": 43, + "src/renderer/src/components/error-boundaries/RecoverableRenderErrorBoundary.lazy-chunk.test.tsx": 86, + "src/renderer/src/components/error-boundaries/RecoverableRenderErrorBoundary.never-landed-reload.test.tsx": 51, + "src/renderer/src/components/error-boundaries/react-185-bystander-attribution.test.tsx": 100, + "src/renderer/src/components/feature-interaction-writer-boundaries.test.ts": 16, + "src/renderer/src/components/feature-tips/CliSkillSetupTerminal.test.tsx": 33, + "src/renderer/src/components/feature-tips/CmdJPaletteFeatureTipVisual.test.tsx": 71, + "src/renderer/src/components/feature-tips/CmdJPaletteTipDialog.test.tsx": 35, + "src/renderer/src/components/feature-tips/VoiceDictationFeatureTipVisual.test.tsx": 72, + "src/renderer/src/components/feature-tips/VoiceDictationTipDialog.test.tsx": 44, + "src/renderer/src/components/feature-tips/feature-tip-cli-install-action.test.ts": 9, + "src/renderer/src/components/feature-tips/feature-tip-modal-state.test.ts": 10, + "src/renderer/src/components/feature-tips/feature-tip-startup-gate.test.ts": 12, + "src/renderer/src/components/feature-tips/feature-tip-telemetry.test.ts": 8, + "src/renderer/src/components/feature-wall/AgentCapabilitiesSetupAction.test.ts": 6, + "src/renderer/src/components/feature-wall/ConnectIntegrationsList.test.tsx": 181, + "src/renderer/src/components/feature-wall/FeatureWallSetupWorkflowActions.test.tsx": 74, + "src/renderer/src/components/feature-wall/FullDiskAccessSetupPrompt.test.ts": 113, + "src/renderer/src/components/feature-wall/KeepAwakeCard.test.tsx": 99, + "src/renderer/src/components/feature-wall/browser-animated-visual-sequence.test.ts": 6, + "src/renderer/src/components/feature-wall/feature-wall-animation-visibility.test.tsx": 163, + "src/renderer/src/components/feature-wall/feature-wall-rail-navigation.test.ts": 9, + "src/renderer/src/components/feature-wall/feature-wall-setup-checklist-localized-copy.test.ts": 9, + "src/renderer/src/components/feature-wall/feature-wall-setup-progress.test.ts": 12, + "src/renderer/src/components/feature-wall/feature-wall-shortcut-labels.test.tsx": 72, + "src/renderer/src/components/feature-wall/feature-wall-usage-tracking.test.ts": 13, + "src/renderer/src/components/feature-wall/use-feature-wall-completion.test.ts": 8, + "src/renderer/src/components/feature-wall/use-feature-wall-tour-telemetry.test.ts": 8, + "src/renderer/src/components/feature-wall/use-integration-connection-status.test.ts": 15, + "src/renderer/src/components/feature-wall/workbench-terminal-storyboard-sequence.test.ts": 9, + "src/renderer/src/components/file-path-cursor-tooltip.test.ts": 5, + "src/renderer/src/components/floating-terminal/FloatingBrowserSlot.test.tsx": 35, + "src/renderer/src/components/floating-terminal/FloatingTerminalOrchestrationDialog.freshness.test.tsx": 163, + "src/renderer/src/components/floating-terminal/FloatingTerminalPanel.bounds.test.tsx": 365, + "src/renderer/src/components/floating-terminal/FloatingTerminalPanel.empty-state.test.tsx": 526, + "src/renderer/src/components/floating-terminal/FloatingTerminalPanel.focus.test.tsx": 615, + "src/renderer/src/components/floating-terminal/FloatingTerminalPanel.markdown-editor.test.tsx": 782, + "src/renderer/src/components/floating-terminal/FloatingTerminalPanel.shortcuts.test.tsx": 878, + "src/renderer/src/components/floating-terminal/FloatingTerminalPanel.tab-lifecycle.test.tsx": 693, + "src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx": 84, + "src/renderer/src/components/floating-terminal/FloatingTerminalWindowControls.test.tsx": 10, + "src/renderer/src/components/floating-terminal/floating-terminal-panel-bounds.test.ts": 17, + "src/renderer/src/components/floating-terminal/floating-terminal-panel-inputs.test.ts": 6, + "src/renderer/src/components/floating-terminal/floating-terminal-panel-view-state.test.ts": 9, + "src/renderer/src/components/floating-terminal/floating-terminal-trigger-position.test.ts": 17, + "src/renderer/src/components/floating-terminal/floating-workspace-tab-reorder.test.ts": 73, + "src/renderer/src/components/floating-terminal/terminal-pane-handle-registry.test.ts": 11, + "src/renderer/src/components/github-body-draft-state.test.ts": 5, + "src/renderer/src/components/github-checks-tab-state.test.ts": 11, + "src/renderer/src/components/github-enterprise-slug-routing-boundary.test.ts": 6, + "src/renderer/src/components/github-item-dialog-source-boundary.test.ts": 13, + "src/renderer/src/components/github-item-dialog/inspect-pull-request/pr-files-combined-diff-section-index.test.tsx": 49, + "src/renderer/src/components/github-link-copy-state.test.ts": 6, + "src/renderer/src/components/github-pr-merge-state.test.ts": 25, + "src/renderer/src/components/github-pr-reviewer-display.test.ts": 11, + "src/renderer/src/components/github-project/GhAuthErrorHelp.test.ts": 57, + "src/renderer/src/components/github-project/ProjectRoadmap.test.tsx": 257, + "src/renderer/src/components/github-project/github-project-picker-filter.test.ts": 10, + "src/renderer/src/components/github-project/group-sort.test.ts": 13, + "src/renderer/src/components/github-project/project-dialog-state.test.ts": 10, + "src/renderer/src/components/github-project/project-picker-browse-cache.test.ts": 14, + "src/renderer/src/components/github-project/project-picker-input.test.ts": 10, + "src/renderer/src/components/github-project/project-row-filtering.test.ts": 14, + "src/renderer/src/components/github-project/project-view-wrapper-source-context-boundary.test.ts": 11, + "src/renderer/src/components/github-project/project-visible-table-cache.test.ts": 6, + "src/renderer/src/components/github/PRFilterPickers.test.ts": 6, + "src/renderer/src/components/github/github-markdown-image-url.test.ts": 5, + "src/renderer/src/components/github/github-mention-option-filter.test.ts": 10, + "src/renderer/src/components/github/github-pr-reviewer-candidate-filter.test.ts": 18, + "src/renderer/src/components/github/github-user-avatar.test.tsx": 66, + "src/renderer/src/components/github/github-work-item-assignee-filter.test.ts": 10, + "src/renderer/src/components/github/github-work-item-label-filter.test.ts": 7, + "src/renderer/src/components/github/pr-comment-code-context.test.ts": 29, + "src/renderer/src/components/github/pr-file-content-size.test.ts": 5, + "src/renderer/src/components/github/repro-8784-ghe-avatar-fallback.test.ts": 13, + "src/renderer/src/components/github/use-image-input.test.ts": 186, + "src/renderer/src/components/hover-reveal-touch-action-visibility.test.ts": 12, + "src/renderer/src/components/jira-create-adf.test.ts": 10, + "src/renderer/src/components/jira-project-picker-filter.test.ts": 11, + "src/renderer/src/components/landing-preflight-dismissal.test.ts": 12, + "src/renderer/src/components/landing-preflight-issues.test.ts": 14, + "src/renderer/src/components/landing-preflight-runtime-boundary.test.ts": 59, + "src/renderer/src/components/linear-api-key-dialog-state.test.ts": 6, + "src/renderer/src/components/linear-issue-attribute-filter-coverage-agreement.test.ts": 1157, + "src/renderer/src/components/linear-issue-attribute-filter-dropdowns.test.tsx": 1107, + "src/renderer/src/components/linear-issue-attribute-filter-primary-team.test.ts": 21, + "src/renderer/src/components/linear-issue-attribute-filter-team-ids.test.ts": 22, + "src/renderer/src/components/linear-issue-project-selector.test.tsx": 88, + "src/renderer/src/components/linear-issue-text-draft-state.test.ts": 4, + "src/renderer/src/components/linear-issue-text-save-plan.test.ts": 6, + "src/renderer/src/components/linear-issue-view-storage.test.ts": 11, + "src/renderer/src/components/linear-issue-workspace-api-parity.test.ts": 9, + "src/renderer/src/components/linear-issue-workspace-detail-state.test.tsx": 47, + "src/renderer/src/components/linear-issue-workspace-header.test.ts": 5, + "src/renderer/src/components/linear-issue-workspace-text.test.ts": 7, + "src/renderer/src/components/linear-project-presentation.test.ts": 9, + "src/renderer/src/components/linear-project-search-query.test.ts": 5, + "src/renderer/src/components/linear-project-view-surfaces.test.tsx": 104, + "src/renderer/src/components/linear-scope-selector.test.ts": 8, + "src/renderer/src/components/link-actions/LinkActionPopover.test.tsx": 167, + "src/renderer/src/components/maintenance/update-card/update-card-error-model.test.ts": 13, + "src/renderer/src/components/mobile/MobileHero.test.tsx": 637, + "src/renderer/src/components/mobile/MobilePage.test.tsx": 685, + "src/renderer/src/components/mobile/MobilePageToolbar.test.tsx": 41, + "src/renderer/src/components/mobile/NetworkInterfacePicker.test.tsx": 77, + "src/renderer/src/components/mobile/WindowsFirewallNotice.test.tsx": 173, + "src/renderer/src/components/mobile/mobile-page-stage.test.ts": 4, + "src/renderer/src/components/mobile/paired-mobile-devices.test.ts": 5, + "src/renderer/src/components/mobile/use-mobile-page-paired-devices.test.ts": 85, + "src/renderer/src/components/mobile/use-mobile-pairing-address-preference.test.tsx": 35, + "src/renderer/src/components/native-chat/NativeChatAutocompleteMenus.test.tsx": 69, + "src/renderer/src/components/native-chat/NativeChatBackgroundTasksStatus.test.tsx": 142, + "src/renderer/src/components/native-chat/NativeChatComposer.test.tsx": 164, + "src/renderer/src/components/native-chat/NativeChatComposerActions.test.tsx": 64, + "src/renderer/src/components/native-chat/NativeChatImageAttachmentPreview.test.tsx": 72, + "src/renderer/src/components/native-chat/NativeChatInteractiveCard.test.tsx": 207, + "src/renderer/src/components/native-chat/NativeChatMessageList.provider-frame.test.tsx": 20, + "src/renderer/src/components/native-chat/NativeChatMessageList.stream-render.perf.test.tsx": 893, + "src/renderer/src/components/native-chat/NativeChatMessageList.task-list-frames.test.tsx": 515, + "src/renderer/src/components/native-chat/NativeChatMessageList.test.tsx": 376, + "src/renderer/src/components/native-chat/NativeChatMessageList.tool-stream-cost.test.tsx": 2900, + "src/renderer/src/components/native-chat/NativeChatMessageList.turn-history.test.tsx": 355, + "src/renderer/src/components/native-chat/NativeChatMessageList.turn-indicator.test.tsx": 374, + "src/renderer/src/components/native-chat/NativeChatMessageList.turn-timing.test.tsx": 126, + "src/renderer/src/components/native-chat/NativeChatMessageList.windowing.test.tsx": 3124, + "src/renderer/src/components/native-chat/NativeChatMessageRow.test.tsx": 180, + "src/renderer/src/components/native-chat/NativeChatMessageTimestamp.test.tsx": 359, + "src/renderer/src/components/native-chat/NativeChatNoticeRow.test.tsx": 192, + "src/renderer/src/components/native-chat/NativeChatPromptEditor.test.tsx": 137, + "src/renderer/src/components/native-chat/NativeChatQuestionCard.test.tsx": 153, + "src/renderer/src/components/native-chat/NativeChatResolutionReceipt.test.tsx": 268, + "src/renderer/src/components/native-chat/NativeChatSessionOptionPickers.test.tsx": 155, + "src/renderer/src/components/native-chat/NativeChatStructuredSession.test.tsx": 509, + "src/renderer/src/components/native-chat/NativeChatStructuredSession.transport-probe.test.tsx": 15241, + "src/renderer/src/components/native-chat/NativeChatStructuredSessionDelivery.test.tsx": 15300, + "src/renderer/src/components/native-chat/NativeChatSubagentRun.test.tsx": 187, + "src/renderer/src/components/native-chat/NativeChatTaskList.test.tsx": 175, + "src/renderer/src/components/native-chat/NativeChatToolRun.identity.test.tsx": 253, + "src/renderer/src/components/native-chat/NativeChatToolRun.test.tsx": 438, + "src/renderer/src/components/native-chat/NativeChatTranscriptChrome.test.tsx": 107, + "src/renderer/src/components/native-chat/NativeChatView.test.tsx": 58, + "src/renderer/src/components/native-chat/StructuredAgentSessionHandoffChrome.test.tsx": 65, + "src/renderer/src/components/native-chat/StructuredAgentSessionPaneOverlayLayer.test.tsx": 49, + "src/renderer/src/components/native-chat/StructuredAgentSessionStatusBridge.test.tsx": 410, + "src/renderer/src/components/native-chat/background-task-roster.test.ts": 21, + "src/renderer/src/components/native-chat/claude-model-switch-confirmation.test.ts": 14, + "src/renderer/src/components/native-chat/claude-terminal-session-options.test.ts": 13, + "src/renderer/src/components/native-chat/native-chat-assembler-merge-parity.test.ts": 8, + "src/renderer/src/components/native-chat/native-chat-attachment-upload.test.ts": 12, + "src/renderer/src/components/native-chat/native-chat-autoscroll.test.ts": 7, + "src/renderer/src/components/native-chat/native-chat-availability.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-composer-autogrow.test.tsx": 178, + "src/renderer/src/components/native-chat/native-chat-composer-composition.test.tsx": 469, + "src/renderer/src/components/native-chat/native-chat-composer-drop-scope.test.tsx": 153, + "src/renderer/src/components/native-chat/native-chat-composer-reveal-focus.test.tsx": 71, + "src/renderer/src/components/native-chat/native-chat-composer-scope-cache.test.ts": 5, + "src/renderer/src/components/native-chat/native-chat-composer-state.test.ts": 25, + "src/renderer/src/components/native-chat/native-chat-diff.test.ts": 13, + "src/renderer/src/components/native-chat/native-chat-dismiss-key.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-draft-cache.test.ts": 6, + "src/renderer/src/components/native-chat/native-chat-file-link.test.ts": 10, + "src/renderer/src/components/native-chat/native-chat-font-scale.test.ts": 11, + "src/renderer/src/components/native-chat/native-chat-http-link-source-owner.test.ts": 13, + "src/renderer/src/components/native-chat/native-chat-image-paste.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-image-runtime-context.test.ts": 8, + "src/renderer/src/components/native-chat/native-chat-incremental-assembler.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-interactive-prompt.test.ts": 17, + "src/renderer/src/components/native-chat/native-chat-launch-default-adoption.test.ts": 14, + "src/renderer/src/components/native-chat/native-chat-launch-draft-resolution.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-launch-draft-send.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-launch-session-options.test.ts": 5, + "src/renderer/src/components/native-chat/native-chat-layout-actions.test.ts": 6, + "src/renderer/src/components/native-chat/native-chat-leaf-routing.test.ts": 6, + "src/renderer/src/components/native-chat/native-chat-live-status.test.ts": 13, + "src/renderer/src/components/native-chat/native-chat-message-grouping.test.ts": 8, + "src/renderer/src/components/native-chat/native-chat-message-list-projection.test.ts": 11, + "src/renderer/src/components/native-chat/native-chat-noise.test.ts": 8, + "src/renderer/src/components/native-chat/native-chat-pagination.test.ts": 5, + "src/renderer/src/components/native-chat/native-chat-pane-resolution.test.ts": 15, + "src/renderer/src/components/native-chat/native-chat-pending-occurrence.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-pending.test.ts": 36, + "src/renderer/src/components/native-chat/native-chat-pinned-rows.test.ts": 6, + "src/renderer/src/components/native-chat/native-chat-preassembled-session-parity.test.ts": 267, + "src/renderer/src/components/native-chat/native-chat-pty-retired-model.test.ts": 12, + "src/renderer/src/components/native-chat/native-chat-pty-session-options.test.ts": 235, + "src/renderer/src/components/native-chat/native-chat-retire-persisted-model.test.ts": 88, + "src/renderer/src/components/native-chat/native-chat-row-height-estimate.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-runtime-owner.test.ts": 7, + "src/renderer/src/components/native-chat/native-chat-runtime-send-launch-draft.test.ts": 1210, + "src/renderer/src/components/native-chat/native-chat-runtime-send.test.ts": 187, + "src/renderer/src/components/native-chat/native-chat-scrape-fallback.test.ts": 11, + "src/renderer/src/components/native-chat/native-chat-send-eligibility.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-send.test.ts": 11, + "src/renderer/src/components/native-chat/native-chat-session-assembler.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-session-option-enrichment.test.ts": 325, + "src/renderer/src/components/native-chat/native-chat-session-option-labels.test.ts": 6, + "src/renderer/src/components/native-chat/native-chat-session-transport.test.ts": 49, + "src/renderer/src/components/native-chat/native-chat-shared-copy-matches-catalog.test.ts": 5, + "src/renderer/src/components/native-chat/native-chat-shortcut.test.ts": 7, + "src/renderer/src/components/native-chat/native-chat-split-shortcut.test.ts": 7, + "src/renderer/src/components/native-chat/native-chat-stop-layering.test.ts": 4, + "src/renderer/src/components/native-chat/native-chat-structured-send-composition-clear.test.tsx": 271, + "src/renderer/src/components/native-chat/native-chat-tab-agent-entry.test.ts": 7, + "src/renderer/src/components/native-chat/native-chat-task-list-history.test.ts": 10, + "src/renderer/src/components/native-chat/native-chat-task-list-state.test.ts": 13, + "src/renderer/src/components/native-chat/native-chat-tool-fold.test.ts": 12, + "src/renderer/src/components/native-chat/native-chat-tool-summary.test.ts": 11, + "src/renderer/src/components/native-chat/native-chat-transcript-slots.test.ts": 6, + "src/renderer/src/components/native-chat/native-chat-turn-diffs.test.ts": 25, + "src/renderer/src/components/native-chat/native-chat-typing-indicator.test.ts": 11, + "src/renderer/src/components/native-chat/native-chat-typing-redirect.test.ts": 5, + "src/renderer/src/components/native-chat/native-chat-view-state.test.ts": 9, + "src/renderer/src/components/native-chat/native-chat-web-link-actions.test.ts": 18, + "src/renderer/src/components/native-chat/native-chat-working-status-shared-clock.test.tsx": 55, + "src/renderer/src/components/native-chat/native-chat-working-suppression.test.ts": 8, + "src/renderer/src/components/native-chat/structured-agent-session-message-projection.test.ts": 8, + "src/renderer/src/components/native-chat/structured-agent-session-read-transport.test.ts": 24, + "src/renderer/src/components/native-chat/structured-session-takeover-report.test.tsx": 74, + "src/renderer/src/components/native-chat/use-native-chat-composer-app-menu-selection.test.tsx": 65, + "src/renderer/src/components/native-chat/use-native-chat-composer-attachments.test.tsx": 83, + "src/renderer/src/components/native-chat/use-native-chat-composer-catalog.test.tsx": 45, + "src/renderer/src/components/native-chat/use-native-chat-composer-keydown.test.tsx": 30, + "src/renderer/src/components/native-chat/use-native-chat-composer-paste.test.tsx": 27, + "src/renderer/src/components/native-chat/use-native-chat-context-menu.test.tsx": 45, + "src/renderer/src/components/native-chat/use-native-chat-external-attachments.test.tsx": 46, + "src/renderer/src/components/native-chat/use-native-chat-hook-status.test.ts": 4, + "src/renderer/src/components/native-chat/use-native-chat-interactive-send.test.tsx": 34, + "src/renderer/src/components/native-chat/use-native-chat-launch-draft-adoption.test.tsx": 51, + "src/renderer/src/components/native-chat/use-native-chat-link-actions.test.tsx": 320, + "src/renderer/src/components/native-chat/use-native-chat-live-session-pending.test.ts": 63, + "src/renderer/src/components/native-chat/use-native-chat-live-session-visibility.test.ts": 75, + "src/renderer/src/components/native-chat/use-native-chat-live-session.test.ts": 158, + "src/renderer/src/components/native-chat/use-native-chat-picker-command-dispatch.test.tsx": 26, + "src/renderer/src/components/native-chat/use-native-chat-retained-session.test.ts": 32, + "src/renderer/src/components/native-chat/use-native-chat-send-lifecycle.test.tsx": 17, + "src/renderer/src/components/native-chat/use-native-chat-session-option-command.test.tsx": 30, + "src/renderer/src/components/native-chat/use-native-chat-session-options.test.ts": 248, + "src/renderer/src/components/native-chat/use-native-chat-skills.react.test.tsx": 396, + "src/renderer/src/components/native-chat/use-native-chat-skills.test.ts": 8, + "src/renderer/src/components/native-chat/use-native-chat-structured-composer-send.test.tsx": 193, + "src/renderer/src/components/native-chat/use-native-chat-toggle-shortcut.test.ts": 10, + "src/renderer/src/components/native-chat/use-native-chat-transcript-window.options.test.tsx": 88, + "src/renderer/src/components/native-chat/use-structured-agent-session-hold.test.tsx": 199, + "src/renderer/src/components/native-chat/use-structured-agent-session-messages.test.tsx": 24, + "src/renderer/src/components/native-chat/use-structured-agent-session-outbox.test.tsx": 870, + "src/renderer/src/components/native-chat/use-structured-agent-session-read.test.tsx": 596, + "src/renderer/src/components/native-chat/use-structured-agent-session.test.tsx": 605, + "src/renderer/src/components/native-chat/use-structured-agent-turn-timing.test.tsx": 23, + "src/renderer/src/components/network/CustomAddressDialog.test.tsx": 72, + "src/renderer/src/components/new-workspace/ComposerParentWorktreePicker.test.tsx": 175, + "src/renderer/src/components/new-workspace/ProjectCombobox.dialog-handoff.test.tsx": 499, + "src/renderer/src/components/new-workspace/ProjectCombobox.test.tsx": 245, + "src/renderer/src/components/new-workspace/ProjectComboboxRow.test.tsx": 65, + "src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx": 999, + "src/renderer/src/components/new-workspace/SmartWorkspaceNameField-provider-boundaries.test.ts": 7, + "src/renderer/src/components/new-workspace/SmartWorkspaceNameField-repo-slug-routing.test.ts": 14, + "src/renderer/src/components/new-workspace/SmartWorkspaceNameField-source-boundaries.test.ts": 5, + "src/renderer/src/components/new-workspace/SmartWorkspaceNameField.ime-enter.test.tsx": 253, + "src/renderer/src/components/new-workspace/SmartWorkspaceNameField.jira-accessibility.test.tsx": 500, + "src/renderer/src/components/new-workspace/project-combobox-matching.test.ts": 29, + "src/renderer/src/components/new-workspace/smart-workspace-command-value.test.ts": 7, + "src/renderer/src/components/new-workspace/smart-workspace-localized-options.test.ts": 59, + "src/renderer/src/components/new-workspace/smart-workspace-source-popover-focus.test.ts": 17, + "src/renderer/src/components/new-workspace/smart-workspace-source-results.test.ts": 16, + "src/renderer/src/components/new-workspace/use-jira-source-connection.test.tsx": 46, + "src/renderer/src/components/new-workspace/use-jira-url-source.test.tsx": 39, + "src/renderer/src/components/new-workspace/use-recent-project-ids.test.ts": 6, + "src/renderer/src/components/onboarding/AgentStep.test.tsx": 78, + "src/renderer/src/components/onboarding/FeatureSetupInlineTerminal.test.tsx": 31, + "src/renderer/src/components/onboarding/NotificationStep.test.tsx": 61, + "src/renderer/src/components/onboarding/OnboardingFlow.test.tsx": 128, + "src/renderer/src/components/onboarding/OnboardingInlineCommandTerminal.command-finished.test.tsx": 47, + "src/renderer/src/components/onboarding/OnboardingInlineCommandTerminal.test.ts": 4, + "src/renderer/src/components/onboarding/ThemeStep.test.ts": 8, + "src/renderer/src/components/onboarding/WindowsTerminalStep.test.tsx": 51, + "src/renderer/src/components/onboarding/agent-picked-payload.test.ts": 10, + "src/renderer/src/components/onboarding/onboarding-dismiss-target.test.ts": 4, + "src/renderer/src/components/onboarding/onboarding-feature-setup.test.ts": 23, + "src/renderer/src/components/onboarding/onboarding-folder-agent-startup.test.ts": 10, + "src/renderer/src/components/onboarding/onboarding-settings-hydration.test.ts": 5, + "src/renderer/src/components/onboarding/use-onboarding-flow-persistence.test.ts": 34, + "src/renderer/src/components/onboarding/use-onboarding-flow.test.ts": 21, + "src/renderer/src/components/onboarding/windows-terminal-onboarding-telemetry.test.ts": 5, + "src/renderer/src/components/orca-profiles/OrcaProfileSignOutConfirmDialog.test.tsx": 21, + "src/renderer/src/components/pet/PetOverlay.frame-durations.test.tsx": 50, + "src/renderer/src/components/pet/PetOverlay.keyframes.test.tsx": 217, + "src/renderer/src/components/pet/PetOverlay.pet-switch.test.tsx": 37, + "src/renderer/src/components/pet/PetOverlay.pointer-interaction.test.tsx": 75, + "src/renderer/src/components/pet/PetOverlay.test.ts": 5, + "src/renderer/src/components/pet/pet-agent-state.test.ts": 14, + "src/renderer/src/components/pet/pet-blob-cache.test.ts": 12, + "src/renderer/src/components/pet/pet-overlay-hit-area.test.tsx": 49, + "src/renderer/src/components/pet/pet-overlay-position.test.ts": 6, + "src/renderer/src/components/pet/pet-overlay-visibility.test.ts": 6, + "src/renderer/src/components/pet/sprite-animation-css.test.ts": 9, + "src/renderer/src/components/pet/usePetPointerInteraction.test.ts": 23, + "src/renderer/src/components/pet/usePetUrl.test.tsx": 15, + "src/renderer/src/components/ports/WorkspacePortScanner.test.tsx": 215, + "src/renderer/src/components/pr-check-counts.test.ts": 11, + "src/renderer/src/components/pr-checks-fix-prompt.test.ts": 20, + "src/renderer/src/components/pr-comments-resolution-prompt.test.ts": 9, + "src/renderer/src/components/provider-check-classification-parity.test.ts": 14, + "src/renderer/src/components/pull-request-page-host-boundary.test.ts": 17, + "src/renderer/src/components/pull-request-page/cache/file-content.test.ts": 11, + "src/renderer/src/components/pull-request-page/files/combined-diff-section-index.test.tsx": 22, + "src/renderer/src/components/pull-request-page/mentions/options.test.ts": 4, + "src/renderer/src/components/pull-request-page/mentions/query.test.ts": 7, + "src/renderer/src/components/pull-request-page/presentation/state-badge.test.ts": 6, + "src/renderer/src/components/quick-open-file-list.react.test.tsx": 90, + "src/renderer/src/components/quick-open-file-list.test.ts": 9, + "src/renderer/src/components/quick-open-install-rg-guidance.render.test.tsx": 57, + "src/renderer/src/components/quick-open-install-rg-guidance.test.ts": 9, + "src/renderer/src/components/quick-open-search.test.ts": 248, + "src/renderer/src/components/repo/NestedRepoChecklist.test.tsx": 17, + "src/renderer/src/components/repo/NestedRepoScanLimitNotice.test.ts": 5, + "src/renderer/src/components/repo/repo-icon.emoji-centering.test.tsx": 45, + "src/renderer/src/components/repo/repo-icon.test.tsx": 61, + "src/renderer/src/components/right-sidebar/ActionButton.test.tsx": 17, + "src/renderer/src/components/right-sidebar/AiVaultSessionRow.test.tsx": 294, + "src/renderer/src/components/right-sidebar/AiVaultSessionSubagents.test.tsx": 67, + "src/renderer/src/components/right-sidebar/ChecksPanel.review-header.test.tsx": 28, + "src/renderer/src/components/right-sidebar/ChecksPanel.updated-at-metadata.test.tsx": 46, + "src/renderer/src/components/right-sidebar/CommitArea.chevron-spinner.test.tsx": 78, + "src/renderer/src/components/right-sidebar/CommitArea.generate.test.tsx": 87, + "src/renderer/src/components/right-sidebar/CommitArea.primary-icons.test.tsx": 92, + "src/renderer/src/components/right-sidebar/CommitArea.test.tsx": 373, + "src/renderer/src/components/right-sidebar/FileExplorerNameFilter.test.tsx": 12, + "src/renderer/src/components/right-sidebar/FileExplorerRow.actions.test.tsx": 19, + "src/renderer/src/components/right-sidebar/FileExplorerToolbar.test.tsx": 45, + "src/renderer/src/components/right-sidebar/FileExplorerViewSwitch.test.tsx": 10, + "src/renderer/src/components/right-sidebar/FileExplorerVirtualRows.row-handlers.test.tsx": 16, + "src/renderer/src/components/right-sidebar/FileExplorerVirtualRowsAddProject.test.tsx": 14, + "src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksPanel.test.tsx": 300, + "src/renderer/src/components/right-sidebar/FolderWorkspacePrChecksRow.test.tsx": 72, + "src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.test.tsx": 69, + "src/renderer/src/components/right-sidebar/GitHistoryPanel.test.tsx": 60, + "src/renderer/src/components/right-sidebar/GitHubPRStackMap.test.tsx": 75, + "src/renderer/src/components/right-sidebar/HostedReviewActions.draft.test.tsx": 22, + "src/renderer/src/components/right-sidebar/PluginPanel.test.tsx": 220, + "src/renderer/src/components/right-sidebar/PortsPanel.test.tsx": 1028, + "src/renderer/src/components/right-sidebar/PullRequestComposer.generate-tooltip.test.tsx": 753, + "src/renderer/src/components/right-sidebar/SearchResultItems.test.tsx": 18, + "src/renderer/src/components/right-sidebar/SessionRowTrailingActions.test.tsx": 54, + "src/renderer/src/components/right-sidebar/SourceControl.branch-line-total.test.tsx": 774, + "src/renderer/src/components/right-sidebar/SourceControl.commit-drafts.test.ts": 18, + "src/renderer/src/components/right-sidebar/SourceControl.commit-failure-recovery.test.ts": 9, + "src/renderer/src/components/right-sidebar/SourceControl.commit-generation-records.test.ts": 8, + "src/renderer/src/components/right-sidebar/SourceControl.compare-summary.test.ts": 14, + "src/renderer/src/components/right-sidebar/SourceControl.host-context-boundary.test.ts": 8, + "src/renderer/src/components/right-sidebar/SourceControl.hosted-review-header-link.test.tsx": 19, + "src/renderer/src/components/right-sidebar/SourceControl.open-file-highlight.test.tsx": 546, + "src/renderer/src/components/right-sidebar/SourceControl.pr-generation-records.test.ts": 15, + "src/renderer/src/components/right-sidebar/SourceControl.preview-open.test.tsx": 833, + "src/renderer/src/components/right-sidebar/SourceControl.push-failure-recovery.test.ts": 8, + "src/renderer/src/components/right-sidebar/SourceControl.remote-action-errors.test.ts": 9, + "src/renderer/src/components/right-sidebar/SourceControl.resolve-conflicts-prompt.test.ts": 8, + "src/renderer/src/components/right-sidebar/SourceControl.virtual-file-list.test.tsx": 2498, + "src/renderer/src/components/right-sidebar/SourceControlAgentActionDialog.test.tsx": 425, + "src/renderer/src/components/right-sidebar/SourceControlAgentActionDialogForm.test.tsx": 74, + "src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialog.test.ts": 94, + "src/renderer/src/components/right-sidebar/active-checks-status.test.ts": 9, + "src/renderer/src/components/right-sidebar/activity-bar-buttons.test.tsx": 59, + "src/renderer/src/components/right-sidebar/activity-bar-overflow.test.ts": 7, + "src/renderer/src/components/right-sidebar/ai-vault-first-prompt-card.test.tsx": 137, + "src/renderer/src/components/right-sidebar/ai-vault-host-scope.test.ts": 32, + "src/renderer/src/components/right-sidebar/ai-vault-original-pane-index.test.ts": 16, + "src/renderer/src/components/right-sidebar/ai-vault-original-pane.test.ts": 7, + "src/renderer/src/components/right-sidebar/ai-vault-scan-issue-state.test.ts": 8, + "src/renderer/src/components/right-sidebar/ai-vault-scope-paths.test.ts": 12, + "src/renderer/src/components/right-sidebar/ai-vault-scope-state.test.ts": 8, + "src/renderer/src/components/right-sidebar/ai-vault-session-continuation.test.ts": 7, + "src/renderer/src/components/right-sidebar/ai-vault-session-deletability.test.ts": 10, + "src/renderer/src/components/right-sidebar/ai-vault-session-display.test.ts": 13, + "src/renderer/src/components/right-sidebar/ai-vault-session-filters.test.ts": 19, + "src/renderer/src/components/right-sidebar/ai-vault-session-identity.test.ts": 9, + "src/renderer/src/components/right-sidebar/ai-vault-session-log-open.test.ts": 15, + "src/renderer/src/components/right-sidebar/ai-vault-session-path-actions.test.ts": 7, + "src/renderer/src/components/right-sidebar/ai-vault-session-projects.test.ts": 22, + "src/renderer/src/components/right-sidebar/ai-vault-session-publication-gate.test.ts": 17, + "src/renderer/src/components/right-sidebar/ai-vault-session-refresh.test.ts": 193, + "src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat-launch.test.ts": 9, + "src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.test.ts": 10, + "src/renderer/src/components/right-sidebar/ai-vault-session-resume.test.ts": 19, + "src/renderer/src/components/right-sidebar/ai-vault-session-worktree-map.test.tsx": 61, + "src/renderer/src/components/right-sidebar/ai-vault-session-worktree.test.ts": 19, + "src/renderer/src/components/right-sidebar/ai-vault-view-defaults.test.ts": 10, + "src/renderer/src/components/right-sidebar/ai-vault-view-options-persistence.test.ts": 13, + "src/renderer/src/components/right-sidebar/branch-line-total-request-gate.test.ts": 6, + "src/renderer/src/components/right-sidebar/check-details-resize.test.ts": 4, + "src/renderer/src/components/right-sidebar/checks-entry-refresh.test.ts": 10, + "src/renderer/src/components/right-sidebar/checks-list-expanded-details.test.tsx": 327, + "src/renderer/src/components/right-sidebar/checks-panel-async-result-key.test.ts": 4, + "src/renderer/src/components/right-sidebar/checks-panel-content.test.tsx": 100, + "src/renderer/src/components/right-sidebar/checks-panel-empty-state.test.ts": 28, + "src/renderer/src/components/right-sidebar/checks-panel-git-status-snapshot.test.ts": 14, + "src/renderer/src/components/right-sidebar/checks-panel-hosted-review-click-routing.test.ts": 10, + "src/renderer/src/components/right-sidebar/checks-panel-pr-refresh-breadcrumb.test.ts": 11, + "src/renderer/src/components/right-sidebar/checks-panel-pr-refresh-request.test.ts": 9, + "src/renderer/src/components/right-sidebar/checks-panel-review-creation.test.ts": 16, + "src/renderer/src/components/right-sidebar/checks-panel-review-lookup-authority.test.ts": 11, + "src/renderer/src/components/right-sidebar/checks-panel-review.test.ts": 8, + "src/renderer/src/components/right-sidebar/checks-panel-terminal-worktree.test.ts": 12, + "src/renderer/src/components/right-sidebar/checks-panel/gitlab-review-client.test.ts": 9, + "src/renderer/src/components/right-sidebar/checks-panel/panel-content-rendering.test.tsx": 90, + "src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-check-and-review-actions.test.tsx": 33, + "src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-create-review.test.tsx": 22, + "src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-generation.test.tsx": 17, + "src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-git-status-effects.test.tsx": 37, + "src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-manual-refresh.test.tsx": 15, + "src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-polling.test.tsx": 38, + "src/renderer/src/components/right-sidebar/coalesced-poll-runner.test.ts": 18, + "src/renderer/src/components/right-sidebar/commit-failure-dialog-state.test.ts": 4, + "src/renderer/src/components/right-sidebar/commit-failure-summary.test.ts": 21, + "src/renderer/src/components/right-sidebar/create-review-draft-title.test.ts": 7, + "src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.test.ts": 6, + "src/renderer/src/components/right-sidebar/discard-all-sequence.test.ts": 19, + "src/renderer/src/components/right-sidebar/file-explorer-add-project-action.test.ts": 7, + "src/renderer/src/components/right-sidebar/file-explorer-batch-deletion.test.ts": 140, + "src/renderer/src/components/right-sidebar/file-explorer-deferred-dir-toggle.test.ts": 42, + "src/renderer/src/components/right-sidebar/file-explorer-dir-load-tracker.test.ts": 4, + "src/renderer/src/components/right-sidebar/file-explorer-dir-toggle-timing.test.ts": 11, + "src/renderer/src/components/right-sidebar/file-explorer-directory-listing.test.ts": 5, + "src/renderer/src/components/right-sidebar/file-explorer-drag-scroll-marker.test.tsx": 56, + "src/renderer/src/components/right-sidebar/file-explorer-entries.test.ts": 9, + "src/renderer/src/components/right-sidebar/file-explorer-expanded-dirs-refresh.test.ts": 23, + "src/renderer/src/components/right-sidebar/file-explorer-inline-input-outside-click.test.tsx": 62, + "src/renderer/src/components/right-sidebar/file-explorer-inline-rename-flow.test.tsx": 113, + "src/renderer/src/components/right-sidebar/file-explorer-keyboard-navigation.test.ts": 11, + "src/renderer/src/components/right-sidebar/file-explorer-name-filter-projection.test.ts": 5, + "src/renderer/src/components/right-sidebar/file-explorer-operation-generation.test.ts": 35, + "src/renderer/src/components/right-sidebar/file-explorer-paths.test.ts": 5, + "src/renderer/src/components/right-sidebar/file-explorer-refresh-concurrency.test.ts": 4, + "src/renderer/src/components/right-sidebar/file-explorer-reset.test.ts": 5, + "src/renderer/src/components/right-sidebar/file-explorer-row-projection.test.ts": 8, + "src/renderer/src/components/right-sidebar/file-explorer-runtime-owner-boundary.test.ts": 9, + "src/renderer/src/components/right-sidebar/file-explorer-selection.test.ts": 8, + "src/renderer/src/components/right-sidebar/file-explorer-stale-dir-cache.test.ts": 5, + "src/renderer/src/components/right-sidebar/file-explorer-watch-drive-root.test.ts": 12, + "src/renderer/src/components/right-sidebar/file-explorer-watch-reconcile.test.ts": 193, + "src/renderer/src/components/right-sidebar/file-explorer-watch-refresh-scheduler.test.ts": 37, + "src/renderer/src/components/right-sidebar/file-search-include-pattern.test.ts": 8, + "src/renderer/src/components/right-sidebar/folder-workspace-attached-worktrees.test.ts": 16, + "src/renderer/src/components/right-sidebar/fork-push-target-label.test.ts": 6, + "src/renderer/src/components/right-sidebar/git-status-push-signal-refresh.test.ts": 32, + "src/renderer/src/components/right-sidebar/git-status-refresh-branch-line-total.test.ts": 19, + "src/renderer/src/components/right-sidebar/git-status-refresh-scheduler.test.ts": 23, + "src/renderer/src/components/right-sidebar/git-status-refresh.test.ts": 66, + "src/renderer/src/components/right-sidebar/github-pr-link-modal.test.ts": 9, + "src/renderer/src/components/right-sidebar/github-pr-stack-merge.test.ts": 12, + "src/renderer/src/components/right-sidebar/gitlab-mr-merge-state.test.ts": 10, + "src/renderer/src/components/right-sidebar/local-workspace-port-sections.test.ts": 8, + "src/renderer/src/components/right-sidebar/parent-pr-checks-projection-selector.test.ts": 77, + "src/renderer/src/components/right-sidebar/parent-pr-checks-refresh.test.ts": 32, + "src/renderer/src/components/right-sidebar/parent-pr-checks-rows.test.ts": 37, + "src/renderer/src/components/right-sidebar/plugin-panel-activity-items.test.ts": 5, + "src/renderer/src/components/right-sidebar/plugin-panel-bridge-host.test.ts": 45, + "src/renderer/src/components/right-sidebar/plugin-panel-watchdog-visibility.test.ts": 12, + "src/renderer/src/components/right-sidebar/plugin-panel-watchdog.test.ts": 12, + "src/renderer/src/components/right-sidebar/pr-comment-presentation.test.ts": 9, + "src/renderer/src/components/right-sidebar/pr-comment-snapshotted-thread-resolver.test.ts": 11, + "src/renderer/src/components/right-sidebar/pr-comment-thread-resolution.test.ts": 4, + "src/renderer/src/components/right-sidebar/pr-comments-ai-launch-ack.test.ts": 23, + "src/renderer/src/components/right-sidebar/pr-comments-list-selection.test.tsx": 791, + "src/renderer/src/components/right-sidebar/push-target-upstream-refresh-cache.test.ts": 11, + "src/renderer/src/components/right-sidebar/review-cache-entry-selection.test.ts": 11, + "src/renderer/src/components/right-sidebar/right-panel-comment-composer.ime-enter.test.tsx": 103, + "src/renderer/src/components/right-sidebar/right-panel-comment-focus-timers.test.ts": 8, + "src/renderer/src/components/right-sidebar/right-sidebar-activity-visibility.test.ts": 5, + "src/renderer/src/components/right-sidebar/right-sidebar-effective-tab.test.ts": 5, + "src/renderer/src/components/right-sidebar/right-sidebar-primary-action-layout.test.ts": 5, + "src/renderer/src/components/right-sidebar/right-sidebar-titlebar-drag-regions.render.test.tsx": 126, + "src/renderer/src/components/right-sidebar/right-sidebar-titlebar-drag-regions.test.ts": 5, + "src/renderer/src/components/right-sidebar/right-sidebar-width.test.ts": 5, + "src/renderer/src/components/right-sidebar/runSourceControlAgentActionStart.test.ts": 11, + "src/renderer/src/components/right-sidebar/search-match-open.test.ts": 12, + "src/renderer/src/components/right-sidebar/search-rows.test.ts": 6, + "src/renderer/src/components/right-sidebar/source-control-action-recipe-match.test.ts": 11, + "src/renderer/src/components/right-sidebar/source-control-actions.test.ts": 5, + "src/renderer/src/components/right-sidebar/source-control-active-open-file-keys.test.ts": 8, + "src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.test.ts": 4, + "src/renderer/src/components/right-sidebar/source-control-branch-context-row.test.tsx": 147, + "src/renderer/src/components/right-sidebar/source-control-branch-context-stats.test.ts": 17, + "src/renderer/src/components/right-sidebar/source-control-branch-section-heading.test.tsx": 23, + "src/renderer/src/components/right-sidebar/source-control-commit-eligibility.test.ts": 7, + "src/renderer/src/components/right-sidebar/source-control-commit-message-rows.test.ts": 99, + "src/renderer/src/components/right-sidebar/source-control-create-pr-intent-flow.test.ts": 18, + "src/renderer/src/components/right-sidebar/source-control-create-pr-intent-state.test.ts": 7, + "src/renderer/src/components/right-sidebar/source-control-create-review-blocked-action.test.ts": 10, + "src/renderer/src/components/right-sidebar/source-control-created-review-link.test.ts": 6, + "src/renderer/src/components/right-sidebar/source-control-discard-confirmation.test.ts": 17, + "src/renderer/src/components/right-sidebar/source-control-discard-dialog.test.tsx": 168, + "src/renderer/src/components/right-sidebar/source-control-dropdown-items.create-pr-intent.test.ts": 24, + "src/renderer/src/components/right-sidebar/source-control-dropdown-items.test.ts": 34, + "src/renderer/src/components/right-sidebar/source-control-entry-actions.test.ts": 5, + "src/renderer/src/components/right-sidebar/source-control-entry-context-menu.test.tsx": 17, + "src/renderer/src/components/right-sidebar/source-control-file-filter.test.ts": 6, + "src/renderer/src/components/right-sidebar/source-control-header-toolbar-identity.test.tsx": 100, + "src/renderer/src/components/right-sidebar/source-control-header-toolbar.test.ts": 6, + "src/renderer/src/components/right-sidebar/source-control-hosted-review-creation-eligibility-snapshot.test.ts": 17, + "src/renderer/src/components/right-sidebar/source-control-hosted-review-push-target.test.ts": 6, + "src/renderer/src/components/right-sidebar/source-control-huge-repo-warning-dismissals.test.ts": 237, + "src/renderer/src/components/right-sidebar/source-control-manual-review-url.test.ts": 13, + "src/renderer/src/components/right-sidebar/source-control-primary-action.create-pr-intent.test.ts": 29, + "src/renderer/src/components/right-sidebar/source-control-primary-action.test.ts": 34, + "src/renderer/src/components/right-sidebar/source-control-push-recovery.test.ts": 8, + "src/renderer/src/components/right-sidebar/source-control-section-order.test.ts": 9, + "src/renderer/src/components/right-sidebar/source-control-split-open.test.ts": 7, + "src/renderer/src/components/right-sidebar/source-control-status-sort.test.ts": 8, + "src/renderer/src/components/right-sidebar/source-control-submodule-expansion.test.ts": 16, + "src/renderer/src/components/right-sidebar/source-control-text-generation-defaults.test.ts": 8, + "src/renderer/src/components/right-sidebar/source-control-too-many-changes-banner.test.tsx": 87, + "src/renderer/src/components/right-sidebar/source-control-tree.test.ts": 13, + "src/renderer/src/components/right-sidebar/source-control-virtual-file-list.test.tsx": 148, + "src/renderer/src/components/right-sidebar/source-control/listing/use-file-projection-work.test.tsx": 31, + "src/renderer/src/components/right-sidebar/source-control/listing/use-store-actions.store-subscriptions.test.tsx": 50, + "src/renderer/src/components/right-sidebar/source-control/review/suppressed-github-pr.test.ts": 10, + "src/renderer/src/components/right-sidebar/source-control/review/use-action-model.test.tsx": 20, + "src/renderer/src/components/right-sidebar/source-control/review/use-hosted-review-state.test.tsx": 38, + "src/renderer/src/components/right-sidebar/status-display.test.ts": 4, + "src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.test.ts": 87, + "src/renderer/src/components/right-sidebar/use-git-status-upstream-ref-watch.test.ts": 11, + "src/renderer/src/components/right-sidebar/use-hosted-review-actions.test.tsx": 47, + "src/renderer/src/components/right-sidebar/use-installed-plugin-route-reconciliation.test.tsx": 23, + "src/renderer/src/components/right-sidebar/use-persisted-ai-vault-view-options.test.tsx": 34, + "src/renderer/src/components/right-sidebar/use-plugin-panel-theme-revision.test.tsx": 47, + "src/renderer/src/components/right-sidebar/use-source-control-ai.test.ts": 6, + "src/renderer/src/components/right-sidebar/use-source-control-branch-compare.test.tsx": 64, + "src/renderer/src/components/right-sidebar/use-source-control-git-history.test.tsx": 45, + "src/renderer/src/components/right-sidebar/useCreatePullRequestDialogFields.repo-default.test.ts": 28, + "src/renderer/src/components/right-sidebar/useCreatePullRequestDialogFields.test.ts": 48, + "src/renderer/src/components/right-sidebar/useFileExplorerDragDrop.test.ts": 5, + "src/renderer/src/components/right-sidebar/useFileExplorerHandlers.test.ts": 14, + "src/renderer/src/components/right-sidebar/useFileExplorerKeys.test.ts": 4, + "src/renderer/src/components/right-sidebar/useFileExplorerRowDrag.test.ts": 20, + "src/renderer/src/components/right-sidebar/useFileExplorerTree.refresh-projection-churn.test.tsx": 35, + "src/renderer/src/components/right-sidebar/useFileExplorerTree.stale-dirs.test.tsx": 37, + "src/renderer/src/components/right-sidebar/useFileExplorerVisibleRowProjection.debounce.test.tsx": 110, + "src/renderer/src/components/right-sidebar/useFileExplorerVisibleRowProjection.test.ts": 12, + "src/renderer/src/components/right-sidebar/useFileExplorerWatch.pending-refresh.test.tsx": 27, + "src/renderer/src/components/right-sidebar/useFileExplorerWatch.test.ts": 12, + "src/renderer/src/components/right-sidebar/useFileSearchRunner.test.tsx": 27, + "src/renderer/src/components/right-sidebar/useGitStatusPolling.rerender.test.ts": 57, + "src/renderer/src/components/right-sidebar/useGitStatusPolling.test.ts": 585, + "src/renderer/src/components/right-sidebar/useHostedReviewStackParent.test.tsx": 33, + "src/renderer/src/components/right-sidebar/useSourceControlSelection.test.ts": 9, + "src/renderer/src/components/right-sidebar/useSourceControlSubmoduleStatus.test.tsx": 39, + "src/renderer/src/components/settings/AccountsPane.test.tsx": 508, + "src/renderer/src/components/settings/AdvancedNetworkSettingsSection.test.ts": 9, + "src/renderer/src/components/settings/AdvancedPane.test.tsx": 37, + "src/renderer/src/components/settings/AgentSkillSetupPanel.freshness.test.tsx": 63, + "src/renderer/src/components/settings/AgentSkillSetupPanel.test.tsx": 479, + "src/renderer/src/components/settings/AgentsPane.test.tsx": 445, + "src/renderer/src/components/settings/AppearancePane.test.tsx": 694, + "src/renderer/src/components/settings/ArtifactsSettingsPane.test.tsx": 358, + "src/renderer/src/components/settings/AutomationsSettingsPane.test.tsx": 117, + "src/renderer/src/components/settings/BranchPrefixFeedback.test.tsx": 16, + "src/renderer/src/components/settings/BrowserClientHostedRemoteSetting.test.tsx": 116, + "src/renderer/src/components/settings/BrowserPane.test.ts": 6, + "src/renderer/src/components/settings/BrowserSshWorkspaceRoutingSetting.test.tsx": 111, + "src/renderer/src/components/settings/BrowserUseSkillStep.test.tsx": 11, + "src/renderer/src/components/settings/CliSection.install-failure.test.tsx": 197, + "src/renderer/src/components/settings/CliSection.test.tsx": 87, + "src/renderer/src/components/settings/CliSkillRuntimeSetup.test.tsx": 60, + "src/renderer/src/components/settings/CloudVmSetupGuide.test.tsx": 28, + "src/renderer/src/components/settings/CommitMessageAiPane.test.tsx": 532, + "src/renderer/src/components/settings/DefaultWindowsProjectRuntimeSetting.test.tsx": 28, + "src/renderer/src/components/settings/DeveloperPermissionsPane.test.tsx": 283, + "src/renderer/src/components/settings/DiffShowWhitespaceSetting.test.tsx": 57, + "src/renderer/src/components/settings/EditorWordWrapSetting.test.tsx": 74, + "src/renderer/src/components/settings/EphemeralVmRuntimesSection.test.tsx": 128, + "src/renderer/src/components/settings/EphemeralVmsPane.test.tsx": 103, + "src/renderer/src/components/settings/ExperimentalPane.test.tsx": 797, + "src/renderer/src/components/settings/FloatingWorkspacePane.test.tsx": 6, + "src/renderer/src/components/settings/GeneralPane.test.ts": 11, + "src/renderer/src/components/settings/GeneralRemoteServerUpdates.test.tsx": 55, + "src/renderer/src/components/settings/GeneralUpdateSettingsSection.test.tsx": 79, + "src/renderer/src/components/settings/GeneralWorkspaceSettingsSection.test.tsx": 308, + "src/renderer/src/components/settings/GhosttyImportModal.test.ts": 17, + "src/renderer/src/components/settings/GitPane.test.ts": 54, + "src/renderer/src/components/settings/GitPane.test.tsx": 109, + "src/renderer/src/components/settings/GrokAccountsSection.test.tsx": 61, + "src/renderer/src/components/settings/HiddenExperimentalGroup.test.tsx": 85, + "src/renderer/src/components/settings/KagiSessionLinkForm.test.ts": 5, + "src/renderer/src/components/settings/LinearAgentSkillGuide.test.tsx": 49, + "src/renderer/src/components/settings/LinearAgentSkillNotes.test.tsx": 27, + "src/renderer/src/components/settings/LinearAgentSkillPane.test.tsx": 152, + "src/renderer/src/components/settings/LocalNetworkConnectionTest.test.tsx": 227, + "src/renderer/src/components/settings/MobileEmulatorAgentControlRow.test.tsx": 40, + "src/renderer/src/components/settings/MobilePairingConnectionOptions.test.tsx": 243, + "src/renderer/src/components/settings/MobilePairingQrSection.test.tsx": 203, + "src/renderer/src/components/settings/MobilePairingSetupSection.test.tsx": 748, + "src/renderer/src/components/settings/MobilePane.test.tsx": 885, + "src/renderer/src/components/settings/MobilePaneAddressSearch.test.tsx": 31, + "src/renderer/src/components/settings/NativeChatSupportedAgents.test.tsx": 114, + "src/renderer/src/components/settings/NotificationsPane.test.tsx": 55, + "src/renderer/src/components/settings/OpenInMenuSetting.test.ts": 23, + "src/renderer/src/components/settings/OrcaAccountSettingsPane.test.tsx": 140, + "src/renderer/src/components/settings/OrchestrationPane.test.tsx": 266, + "src/renderer/src/components/settings/OrchestrationSkillAgentCoverage.test.tsx": 26, + "src/renderer/src/components/settings/PluginConsentDialog.test.tsx": 450, + "src/renderer/src/components/settings/PluginInstallDialog.test.tsx": 165, + "src/renderer/src/components/settings/PluginKeybindingConsentPreview.test.ts": 7, + "src/renderer/src/components/settings/PluginMarketplaceBrowser.test.tsx": 397, + "src/renderer/src/components/settings/PluginMarketplaceSourceDialog.test.tsx": 195, + "src/renderer/src/components/settings/PluginSettingsRow.test.tsx": 75, + "src/renderer/src/components/settings/PluginsSettingsSection.lifecycle.test.tsx": 748, + "src/renderer/src/components/settings/PrivacyPane.test.ts": 47, + "src/renderer/src/components/settings/ProjectWindowsRuntimeSetting.test.tsx": 159, + "src/renderer/src/components/settings/QuickCommandsList.test.tsx": 122, + "src/renderer/src/components/settings/QuickCommandsPane.test.ts": 8, + "src/renderer/src/components/settings/RemoteServerUpdateDialog.test.tsx": 69, + "src/renderer/src/components/settings/RepositoryForkSyncSection.test.tsx": 62, + "src/renderer/src/components/settings/RepositoryHooksSection.test.ts": 120, + "src/renderer/src/components/settings/RepositoryHostSetupsSection.test.tsx": 354, + "src/renderer/src/components/settings/RepositoryHostSetupsSection.workspace-window.test.tsx": 71, + "src/renderer/src/components/settings/RepositoryIconEmojiPicker.test.tsx": 37, + "src/renderer/src/components/settings/RepositoryIconPicker.github-avatar-refresh.test.tsx": 47, + "src/renderer/src/components/settings/RepositoryPane.test.ts": 837, + "src/renderer/src/components/settings/RepositoryPaneDraftInput.test.tsx": 74, + "src/renderer/src/components/settings/RepositorySourceControlAiSection.test.ts": 73, + "src/renderer/src/components/settings/RepositoryWorktreeDefaultsSection.test.tsx": 130, + "src/renderer/src/components/settings/RuntimeEnvironmentsPane.test.ts": 17, + "src/renderer/src/components/settings/RuntimeHostAccessForm.test.tsx": 75, + "src/renderer/src/components/settings/RuntimePairingGeneratorForm.test.tsx": 41, + "src/renderer/src/components/settings/RuntimePairingUrlGenerator.test.tsx": 88, + "src/renderer/src/components/settings/Settings.load-performance.test.ts": 7, + "src/renderer/src/components/settings/SettingsConstants.test.ts": 9, + "src/renderer/src/components/settings/SettingsFormControls.font-autocomplete.test.tsx": 359, + "src/renderer/src/components/settings/SettingsFormControls.number-field.test.tsx": 66, + "src/renderer/src/components/settings/SettingsFormControls.segmented-control.test.tsx": 69, + "src/renderer/src/components/settings/SettingsSidebar.test.tsx": 79, + "src/renderer/src/components/settings/ShareSkillsSettingsPane.test.tsx": 192, + "src/renderer/src/components/settings/ShortcutCommandBlock.test.tsx": 76, + "src/renderer/src/components/settings/ShortcutFilterRail.test.ts": 10, + "src/renderer/src/components/settings/SourceControlActionRepoOverrideNote.test.tsx": 137, + "src/renderer/src/components/settings/SparsePresetSettingsSection.test.tsx": 28, + "src/renderer/src/components/settings/SshTargetCard.test.tsx": 89, + "src/renderer/src/components/settings/SshTargetForm.test.tsx": 363, + "src/renderer/src/components/settings/TaskSourceLinearSetup.test.tsx": 77, + "src/renderer/src/components/settings/TaskSourceProviderCard.test.tsx": 72, + "src/renderer/src/components/settings/TaskSourceShowInTasksStep.test.tsx": 32, + "src/renderer/src/components/settings/TaskSourceSimpleSetup.test.tsx": 40, + "src/renderer/src/components/settings/TasksPane.test.tsx": 282, + "src/renderer/src/components/settings/TerminalAdvancedSection.test.tsx": 85, + "src/renderer/src/components/settings/TerminalAppearanceSection.ghostty.test.ts": 48, + "src/renderer/src/components/settings/TerminalContrastSetting.test.tsx": 128, + "src/renderer/src/components/settings/TerminalFontSizeSetting.test.tsx": 62, + "src/renderer/src/components/settings/TerminalPane.pwsh.test.ts": 38, + "src/renderer/src/components/settings/TerminalSettingsPreview.lifecycle.test.tsx": 19, + "src/renderer/src/components/settings/TerminalTccAttributionNotice.test.tsx": 89, + "src/renderer/src/components/settings/TerminalThemeSections.lifecycle.test.ts": 51, + "src/renderer/src/components/settings/VoicePane.test.tsx": 443, + "src/renderer/src/components/settings/VoiceSpeechModelSection.test.tsx": 85, + "src/renderer/src/components/settings/WorkspaceDirectorySetting.test.tsx": 104, + "src/renderer/src/components/settings/accounts-search.test.ts": 7, + "src/renderer/src/components/settings/agent-availability-settings.test.ts": 19, + "src/renderer/src/components/settings/agent-default-env-draft.test.ts": 9, + "src/renderer/src/components/settings/agent-skill-installed-command-callers.test.ts": 103, + "src/renderer/src/components/settings/appearance-interface-summary.test.ts": 7, + "src/renderer/src/components/settings/appearance-search.test.ts": 315, + "src/renderer/src/components/settings/appearance-status-bar-search.test.ts": 8, + "src/renderer/src/components/settings/appearance-usage-percentage-search.test.ts": 3, + "src/renderer/src/components/settings/browser-cookie-import-label.test.ts": 9, + "src/renderer/src/components/settings/browser-link-routing-localization.test.ts": 222, + "src/renderer/src/components/settings/browser-search.test.ts": 60, + "src/renderer/src/components/settings/browser-session-host-selection.test.ts": 4, + "src/renderer/src/components/settings/cli-install-failure.test.ts": 7, + "src/renderer/src/components/settings/cli-source-control-integration-cards.test.tsx": 41, + "src/renderer/src/components/settings/codex-account-auth-warning.test.ts": 11, + "src/renderer/src/components/settings/codex-config-sync-warning.test.ts": 7, + "src/renderer/src/components/settings/codex-session-source-home-control.test.tsx": 10, + "src/renderer/src/components/settings/developer-permissions-search.test.ts": 280, + "src/renderer/src/components/settings/host-scoped-setting-scope.test.ts": 9, + "src/renderer/src/components/settings/integrations-pane-status.test.ts": 10, + "src/renderer/src/components/settings/jira-integration-card.test.tsx": 71, + "src/renderer/src/components/settings/linear-agent-skill-install-cta.test.tsx": 126, + "src/renderer/src/components/settings/mobile-network-interface-selection.test.ts": 9, + "src/renderer/src/components/settings/mobile-pairing-device-polling.test.ts": 6, + "src/renderer/src/components/settings/mobile-pane-search.test.ts": 9, + "src/renderer/src/components/settings/native-chat-experimental-search-entry.test.ts": 11, + "src/renderer/src/components/settings/osc52-clipboard-copy.test.ts": 7, + "src/renderer/src/components/settings/plugin-install-source.test.ts": 9, + "src/renderer/src/components/settings/provider-account-scope.test.ts": 12, + "src/renderer/src/components/settings/provider-account-visibility.test.ts": 5, + "src/renderer/src/components/settings/provider-rate-limit-scope-panels.test.tsx": 31, + "src/renderer/src/components/settings/repository-hook-settings-draft.test.ts": 7, + "src/renderer/src/components/settings/repository-host-setup-options.test.ts": 6, + "src/renderer/src/components/settings/repository-icon-github.test.ts": 8, + "src/renderer/src/components/settings/repository-source-control-ai-global-ux.test.ts": 52, + "src/renderer/src/components/settings/repository-source-control-ai-persist-queue.test.ts": 10, + "src/renderer/src/components/settings/setting-ownership.test.ts": 13, + "src/renderer/src/components/settings/settings-deep-link-target-watcher.test.ts": 18, + "src/renderer/src/components/settings/settings-form-option-filter.test.ts": 13, + "src/renderer/src/components/settings/settings-project-list.test.ts": 21, + "src/renderer/src/components/settings/settings-search-keywords.test.ts": 122, + "src/renderer/src/components/settings/settings-search.test.ts": 9, + "src/renderer/src/components/settings/settings-setup-guide-progress-hook.test.tsx": 12, + "src/renderer/src/components/settings/settings-setup-guide-progress.test.ts": 5, + "src/renderer/src/components/settings/shortcut-binding-list-mutations.test.ts": 10, + "src/renderer/src/components/settings/shortcut-definition-catalog.test.ts": 269, + "src/renderer/src/components/settings/shortcut-groups.test.ts": 14, + "src/renderer/src/components/settings/shortcut-recording-state.test.ts": 5, + "src/renderer/src/components/settings/shortcut-row-visibility.test.ts": 14, + "src/renderer/src/components/settings/sparse-preset-date.test.ts": 26, + "src/renderer/src/components/settings/sparse-preset-operation-error.test.ts": 3, + "src/renderer/src/components/settings/ssh-target-action-state.test.ts": 4, + "src/renderer/src/components/settings/ssh-target-draft.test.ts": 17, + "src/renderer/src/components/settings/ssh-target-remove.test.ts": 10, + "src/renderer/src/components/settings/ssh-target-save-payload.test.ts": 10, + "src/renderer/src/components/settings/task-source-setup-state.test.ts": 11, + "src/renderer/src/components/settings/task-tracker-integration-cards.test.tsx": 134, + "src/renderer/src/components/settings/terminal-preview-content.test.ts": 7, + "src/renderer/src/components/settings/terminal-search.test.ts": 56, + "src/renderer/src/components/settings/use-debounced-settings-text-draft.test.ts": 35, + "src/renderer/src/components/settings/use-mac-captured-digit-chords.test.ts": 393, + "src/renderer/src/components/settings/use-repository-hook-settings-draft.test.tsx": 34, + "src/renderer/src/components/settings/use-task-source-provider-readiness.test.tsx": 25, + "src/renderer/src/components/settings/useGhosttyImport.test.ts": 17, + "src/renderer/src/components/settings/useWarpThemeImport.test.ts": 31, + "src/renderer/src/components/settings/worktree-symlink-path-filter.test.ts": 11, + "src/renderer/src/components/setup-guide/SetupGuideModal.mount-gating.test.tsx": 1073, + "src/renderer/src/components/setup-guide/use-setup-guide-progress.test.ts": 10, + "src/renderer/src/components/setup-guide/use-setup-guide-telemetry.test.ts": 10, + "src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts": 184, + "src/renderer/src/components/shared/useDaemonActions.test.tsx": 27, + "src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx": 198, + "src/renderer/src/components/sidebar/AddRemoteHostDialog.config-picker.test.tsx": 603, + "src/renderer/src/components/sidebar/AddRemoteHostFields.test.tsx": 39, + "src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx": 54, + "src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts": 8, + "src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx": 71, + "src/renderer/src/components/sidebar/AddRepoHostSelector.test.tsx": 43, + "src/renderer/src/components/sidebar/AddRepoNestedImportStep.test.tsx": 146, + "src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx": 283, + "src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts": 103, + "src/renderer/src/components/sidebar/AgentDashboardSidebarHost.test.tsx": 40, + "src/renderer/src/components/sidebar/AutoRenameFailedDialog.test.tsx": 229, + "src/renderer/src/components/sidebar/CacheTimer.test.tsx": 15, + "src/renderer/src/components/sidebar/CommentMarkdown.github-attachment-image.test.tsx": 68, + "src/renderer/src/components/sidebar/CommentMarkdown.link-click.test.tsx": 134, + "src/renderer/src/components/sidebar/CommentMarkdown.test.tsx": 309, + "src/renderer/src/components/sidebar/DeleteWorktreeDialog.host-context-boundary.test.ts": 7, + "src/renderer/src/components/sidebar/DeleteWorktreeDialog.test.tsx": 523, + "src/renderer/src/components/sidebar/DeleteWorktreeTargetPreview.test.tsx": 105, + "src/renderer/src/components/sidebar/FilterToggleRow.test.tsx": 30, + "src/renderer/src/components/sidebar/ImportedWorktreesVisibilityLine.test.tsx": 34, + "src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.reminder-toast.test.tsx": 214, + "src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.test.tsx": 820, + "src/renderer/src/components/sidebar/LinearAgentSkillSetupPrompt.update-command.test.tsx": 242, + "src/renderer/src/components/sidebar/MarkdownImageLightbox.test.tsx": 265, + "src/renderer/src/components/sidebar/NewExternalWorktreesInboxLine.test.tsx": 109, + "src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx": 80, + "src/renderer/src/components/sidebar/NoticeHostGlyph.test.tsx": 59, + "src/renderer/src/components/sidebar/OrcaYamlTrustDialog.test.tsx": 24, + "src/renderer/src/components/sidebar/ProjectAddedDialog.test.tsx": 37, + "src/renderer/src/components/sidebar/ProjectGroupDeleteDialog.test.tsx": 194, + "src/renderer/src/components/sidebar/ProjectHeaderActions.test.tsx": 17, + "src/renderer/src/components/sidebar/RemoteFileBrowser.paste.test.tsx": 145, + "src/renderer/src/components/sidebar/RemoveFolderDialog.test.tsx": 15, + "src/renderer/src/components/sidebar/SetupGuideSidebarEntry.test.tsx": 65, + "src/renderer/src/components/sidebar/SetupScriptPromptCard.test.ts": 8, + "src/renderer/src/components/sidebar/SetupScriptPromptCardShell.test.tsx": 57, + "src/renderer/src/components/sidebar/Sidebar.test.tsx": 80, + "src/renderer/src/components/sidebar/SidebarAgentsList.test.tsx": 205, + "src/renderer/src/components/sidebar/SidebarFeedbackDialog.test.tsx": 312, + "src/renderer/src/components/sidebar/SidebarGroupByToggle.test.tsx": 62, + "src/renderer/src/components/sidebar/SidebarHeader.test.tsx": 200, + "src/renderer/src/components/sidebar/SidebarNav.test.tsx": 749, + "src/renderer/src/components/sidebar/SidebarRepositoryFilterSection.test.tsx": 507, + "src/renderer/src/components/sidebar/SidebarSettingsHelpMenu.test.tsx": 280, + "src/renderer/src/components/sidebar/SidebarToolbar.test.tsx": 129, + "src/renderer/src/components/sidebar/SidebarWorkspaceFilterSection.test.tsx": 123, + "src/renderer/src/components/sidebar/StatusIndicator.test.ts": 32, + "src/renderer/src/components/sidebar/WorkspaceKanbanCard.host-identity.test.tsx": 30, + "src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.mount-gating.test.tsx": 522, + "src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.search.test.tsx": 179, + "src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.task-status-sync.test.tsx": 128, + "src/renderer/src/components/sidebar/WorkspaceKanbanDrawerHeader.test.tsx": 14, + "src/renderer/src/components/sidebar/WorkspaceKanbanLaneCardList.test.tsx": 70, + "src/renderer/src/components/sidebar/WorkspaceKanbanLaneGrid.test.tsx": 78, + "src/renderer/src/components/sidebar/WorkspaceKanbanSearchField.test.tsx": 80, + "src/renderer/src/components/sidebar/WorkspaceKanbanSettingsMenu.test.tsx": 211, + "src/renderer/src/components/sidebar/WorkspaceKanbanStatusLane.test.tsx": 55, + "src/renderer/src/components/sidebar/WorktreeCard.affiliate-list-mode.test.tsx": 80, + "src/renderer/src/components/sidebar/WorktreeCard.compact-hover.test.tsx": 3366, + "src/renderer/src/components/sidebar/WorktreeCard.compact-ports-hover-independence.test.tsx": 1185, + "src/renderer/src/components/sidebar/WorktreeCard.hosted-review-refresh.test.tsx": 1294, + "src/renderer/src/components/sidebar/WorktreeCard.lineage.test.tsx": 1219, + "src/renderer/src/components/sidebar/WorktreeCard.merged-pr-display.test.tsx": 1041, + "src/renderer/src/components/sidebar/WorktreeCard.pinned-repo-icon.test.tsx": 1063, + "src/renderer/src/components/sidebar/WorktreeCard.pr-display.test.tsx": 1918, + "src/renderer/src/components/sidebar/WorktreeCard.quick-actions.test.tsx": 1401, + "src/renderer/src/components/sidebar/WorktreeCard.ssh-reconnect-prompt.test.tsx": 762, + "src/renderer/src/components/sidebar/WorktreeCard.test.ts": 9, + "src/renderer/src/components/sidebar/WorktreeCardAgents.activation.test.tsx": 523, + "src/renderer/src/components/sidebar/WorktreeCardAgents.expansion-remount.test.tsx": 470, + "src/renderer/src/components/sidebar/WorktreeCardAgents.send-target.test.tsx": 808, + "src/renderer/src/components/sidebar/WorktreeCardAgents.test.tsx": 673, + "src/renderer/src/components/sidebar/WorktreeCardAutomationDetailSection.test.tsx": 86, + "src/renderer/src/components/sidebar/WorktreeCardDisplayMenuSection.test.tsx": 76, + "src/renderer/src/components/sidebar/WorktreeCardMeta.interaction.test.tsx": 305, + "src/renderer/src/components/sidebar/WorktreeCardMeta.test.tsx": 99, + "src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx": 1109, + "src/renderer/src/components/sidebar/WorktreeCardSshHostControl.test.tsx": 281, + "src/renderer/src/components/sidebar/WorktreeCardStatusSlot.test.tsx": 53, + "src/renderer/src/components/sidebar/WorktreeContextMenu.delete-shortcut.test.tsx": 70, + "src/renderer/src/components/sidebar/WorktreeContextMenu.react185-bystander.test.tsx": 733, + "src/renderer/src/components/sidebar/WorktreeContextMenu.test.ts": 31, + "src/renderer/src/components/sidebar/WorktreeDeveloperMenu.test.tsx": 23, + "src/renderer/src/components/sidebar/WorktreeDeveloperMenuReveal.test.tsx": 73, + "src/renderer/src/components/sidebar/WorktreeList.card-memo-stability.test.tsx": 1046, + "src/renderer/src/components/sidebar/WorktreeList.empty-project-rows.test.ts": 1389, + "src/renderer/src/components/sidebar/WorktreeList.folder-workspace-rows.test.ts": 1843, + "src/renderer/src/components/sidebar/WorktreeList.group-headers.test.ts": 1975, + "src/renderer/src/components/sidebar/WorktreeList.lineage-agent-expansion-coupling.test.tsx": 2544, + "src/renderer/src/components/sidebar/WorktreeList.lineage-child-card.test.ts": 1842, + "src/renderer/src/components/sidebar/WorktreeList.lineage-child-real-card.test.tsx": 3279, + "src/renderer/src/components/sidebar/WorktreeList.status-lane-lineage-drop.test.tsx": 2529, + "src/renderer/src/components/sidebar/WorktreeMetaDialog.test.tsx": 1032, + "src/renderer/src/components/sidebar/WorktreeOpenInMenu.test.tsx": 30, + "src/renderer/src/components/sidebar/WorktreeParentPickerPopover.test.ts": 21, + "src/renderer/src/components/sidebar/WorktreeTitleInlineRename.begin-editing.test.tsx": 78, + "src/renderer/src/components/sidebar/WorktreeTitleInlineRename.editor-lifecycle.test.tsx": 147, + "src/renderer/src/components/sidebar/WorktreeTitleInlineRename.test.tsx": 24, + "src/renderer/src/components/sidebar/WorktreeVisibilityDialog.test.tsx": 2347, + "src/renderer/src/components/sidebar/WorktreeVisibilityHelpPopover.test.tsx": 496, + "src/renderer/src/components/sidebar/WorktreeVisibilitySourceList.test.tsx": 107, + "src/renderer/src/components/sidebar/active-worktree-focus-after-delete.test.ts": 13, + "src/renderer/src/components/sidebar/add-remote-host-ssh-actions.test.ts": 18, + "src/renderer/src/components/sidebar/add-repo-browse-authority.test.ts": 9, + "src/renderer/src/components/sidebar/add-repo-existing-workspaces-telemetry.test.ts": 15, + "src/renderer/src/components/sidebar/add-repo-skip-finalization.test.ts": 6, + "src/renderer/src/components/sidebar/clone-defaults.test.ts": 6, + "src/renderer/src/components/sidebar/create-project-defaults.test.ts": 7, + "src/renderer/src/components/sidebar/default-branch-visible-under-hide-sleeping.test.ts": 12, + "src/renderer/src/components/sidebar/delete-worktree-dirty-change-counts.test.ts": 3, + "src/renderer/src/components/sidebar/delete-worktree-failure-toast.test.tsx": 44, + "src/renderer/src/components/sidebar/delete-worktree-flow.test.ts": 284, + "src/renderer/src/components/sidebar/delete-worktree-parallel-flow.test.ts": 37, + "src/renderer/src/components/sidebar/delete-worktree-toast.test.ts": 8, + "src/renderer/src/components/sidebar/empty-project-placeholder-repos.test.ts": 10, + "src/renderer/src/components/sidebar/focused-agent-row-highlight.test.ts": 10, + "src/renderer/src/components/sidebar/folder-workspace-card-pr-display.test.ts": 18, + "src/renderer/src/components/sidebar/folder-workspace-composer-helpers.test.ts": 10, + "src/renderer/src/components/sidebar/folder-workspace-composer-path-status.test.tsx": 48, + "src/renderer/src/components/sidebar/folder-workspace-composer-submit.test.ts": 61, + "src/renderer/src/components/sidebar/folder-workspace-linked-startup-plan.test.ts": 6, + "src/renderer/src/components/sidebar/host-header-drag.test.tsx": 51, + "src/renderer/src/components/sidebar/host-header-menu-items.test.ts": 8, + "src/renderer/src/components/sidebar/host-rename-remove.test.ts": 8, + "src/renderer/src/components/sidebar/host-section-folder-workspace-counts.test.ts": 7, + "src/renderer/src/components/sidebar/host-section-order.test.ts": 5, + "src/renderer/src/components/sidebar/host-section-rows.test.ts": 12, + "src/renderer/src/components/sidebar/hovered-workspace-delete.test.ts": 115, + "src/renderer/src/components/sidebar/imported-worktrees-card-actions.test.ts": 13, + "src/renderer/src/components/sidebar/imported-worktrees-card-candidates.test.ts": 8, + "src/renderer/src/components/sidebar/linear-agent-skill-runtime.shell-override.test.ts": 5, + "src/renderer/src/components/sidebar/linear-agent-skill-setup-reminders.test.ts": 13, + "src/renderer/src/components/sidebar/local-base-ref-suggestion-toast.test.tsx": 52, + "src/renderer/src/components/sidebar/mobile-sidebar-onboarding-badge.test.ts": 34, + "src/renderer/src/components/sidebar/natural-worktree-ids.test.ts": 8, + "src/renderer/src/components/sidebar/new-external-worktrees-inbox-actions.test.ts": 10, + "src/renderer/src/components/sidebar/new-external-worktrees-inbox-candidates.test.ts": 7, + "src/renderer/src/components/sidebar/pinned-section-worktrees.test.ts": 363, + "src/renderer/src/components/sidebar/preserved-branch-batch-toast.test.tsx": 129, + "src/renderer/src/components/sidebar/preserved-branch-toast.test.tsx": 47, + "src/renderer/src/components/sidebar/project-added-default-checkout.test.ts": 22, + "src/renderer/src/components/sidebar/project-group-header-dom.test.ts": 6, + "src/renderer/src/components/sidebar/project-group-header-drag-commit.test.ts": 12, + "src/renderer/src/components/sidebar/project-group-header-drag-start.test.ts": 21, + "src/renderer/src/components/sidebar/project-group-header-drag.test.ts": 16, + "src/renderer/src/components/sidebar/project-group-header-drop.test.ts": 13, + "src/renderer/src/components/sidebar/project-header-action-selector-lockstep.test.ts": 7, + "src/renderer/src/components/sidebar/project-header-color.test.ts": 7, + "src/renderer/src/components/sidebar/project-header-drag-commit.test.ts": 12, + "src/renderer/src/components/sidebar/project-header-drag-start.test.ts": 11, + "src/renderer/src/components/sidebar/project-header-drag.test.ts": 14, + "src/renderer/src/components/sidebar/project-header-drop.test.ts": 7, + "src/renderer/src/components/sidebar/project-order-manual-default-notice-visibility.test.ts": 7, + "src/renderer/src/components/sidebar/prompt-cache-countdown-clock.test.ts": 11, + "src/renderer/src/components/sidebar/prompt-cache-timer-selection.test.ts": 5, + "src/renderer/src/components/sidebar/remote-file-browser-drive-paths.test.ts": 6, + "src/renderer/src/components/sidebar/remote-file-browser-helpers.test.ts": 39, + "src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.test.ts": 42, + "src/renderer/src/components/sidebar/repo-header-create-state.test.ts": 10, + "src/renderer/src/components/sidebar/sidebar-empty-state-gate.test.ts": 4, + "src/renderer/src/components/sidebar/sidebar-filter-state.test.ts": 9, + "src/renderer/src/components/sidebar/sidebar-host-options.test.ts": 15, + "src/renderer/src/components/sidebar/sidebar-project-drop.test.ts": 10, + "src/renderer/src/components/sidebar/sidebar-resize-handle.test.ts": 8, + "src/renderer/src/components/sidebar/sidebar-workspace-option-items.test.ts": 9, + "src/renderer/src/components/sidebar/sleep-worktree-activation-race.test.ts": 17, + "src/renderer/src/components/sidebar/sleep-worktree-flow.test.ts": 19, + "src/renderer/src/components/sidebar/smart-attention.test.ts": 26, + "src/renderer/src/components/sidebar/smart-sort.test.ts": 17, + "src/renderer/src/components/sidebar/ssh-host-remove-resolution.test.ts": 10, + "src/renderer/src/components/sidebar/ssh-target-duplicate.test.ts": 8, + "src/renderer/src/components/sidebar/ssh-workspace-forget-resolution.test.ts": 5, + "src/renderer/src/components/sidebar/stale-agent-row-unverifiable.test.ts": 13, + "src/renderer/src/components/sidebar/truncated-sidebar-label.test.tsx": 63, + "src/renderer/src/components/sidebar/use-add-repo-host-selection.test.ts": 157, + "src/renderer/src/components/sidebar/use-add-repo-hosted-controller.test.ts": 5, + "src/renderer/src/components/sidebar/use-feedback-image-drop.test.tsx": 46, + "src/renderer/src/components/sidebar/use-visible-workspace-kanban-worktree-ids.test.tsx": 28, + "src/renderer/src/components/sidebar/use-workspace-kanban-card-pointer-drag.test.ts": 10, + "src/renderer/src/components/sidebar/use-workspace-kanban-drawer-lingering.test.tsx": 28, + "src/renderer/src/components/sidebar/use-workspace-kanban-outside-dismiss.test.ts": 7, + "src/renderer/src/components/sidebar/use-workspace-kanban-selection.test.tsx": 21, + "src/renderer/src/components/sidebar/use-workspace-reveal-body-redirect.test.tsx": 33, + "src/renderer/src/components/sidebar/use-workspace-status-drop.test.ts": 7, + "src/renderer/src/components/sidebar/use-worktree-activity-status.test.tsx": 23, + "src/renderer/src/components/sidebar/use-worktree-activity-statuses.test.ts": 7, + "src/renderer/src/components/sidebar/use-worktree-card-secondary-details.store-subscriptions.test.tsx": 30, + "src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts": 114, + "src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts": 127, + "src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts": 13, + "src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts": 54, + "src/renderer/src/components/sidebar/useCreateProjectDefaults.test.ts": 30, + "src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts": 105, + "src/renderer/src/components/sidebar/useRenderedSetupScriptPromptState.test.ts": 20, + "src/renderer/src/components/sidebar/useSetupScriptPromptRevalidation.test.tsx": 63, + "src/renderer/src/components/sidebar/useWorkspaceBoardPanel.test.tsx": 68, + "src/renderer/src/components/sidebar/useWorktreeAgentRows.test.ts": 21, + "src/renderer/src/components/sidebar/visible-worktree-activity-inputs.test.ts": 9, + "src/renderer/src/components/sidebar/visible-worktree-indexes.test.ts": 12, + "src/renderer/src/components/sidebar/visible-worktrees.test.ts": 22, + "src/renderer/src/components/sidebar/workspace-board-task-status-sync.test.ts": 26, + "src/renderer/src/components/sidebar/workspace-creator-visibility.test.ts": 6, + "src/renderer/src/components/sidebar/workspace-delete-lineage.test.ts": 9, + "src/renderer/src/components/sidebar/workspace-kanban-area-selection.test.ts": 13, + "src/renderer/src/components/sidebar/workspace-kanban-card-pointer-drag-dom.test.ts": 7, + "src/renderer/src/components/sidebar/workspace-kanban-filtered-drop-index.test.ts": 11, + "src/renderer/src/components/sidebar/workspace-kanban-search.test.ts": 23, + "src/renderer/src/components/sidebar/workspace-kanban-sidebar-drop.test.ts": 21, + "src/renderer/src/components/sidebar/workspace-kanban-virtual-lane-layout.test.ts": 13, + "src/renderer/src/components/sidebar/workspace-kanban-worktree-groups.test.ts": 16, + "src/renderer/src/components/sidebar/workspace-lineage-menu-actions.test.ts": 9, + "src/renderer/src/components/sidebar/workspace-status.test.ts": 11, + "src/renderer/src/components/sidebar/worktree-agent-activity-summary.test.ts": 25, + "src/renderer/src/components/sidebar/worktree-agent-freshness-selector.test.ts": 10, + "src/renderer/src/components/sidebar/worktree-agent-orchestration-batch.test.ts": 17, + "src/renderer/src/components/sidebar/worktree-agent-orchestration-index.test.ts": 103, + "src/renderer/src/components/sidebar/worktree-agent-row-selectors.test.ts": 26, + "src/renderer/src/components/sidebar/worktree-card-agent-ack-inputs.test.tsx": 20, + "src/renderer/src/components/sidebar/worktree-card-agent-summary.test.ts": 31, + "src/renderer/src/components/sidebar/worktree-card-compact-agent-row.stable-message.test.tsx": 84, + "src/renderer/src/components/sidebar/worktree-card-details-hover-state.test.tsx": 26, + "src/renderer/src/components/sidebar/worktree-card-dom-events.test.ts": 6, + "src/renderer/src/components/sidebar/worktree-card-jira-issue-display.test.ts": 6, + "src/renderer/src/components/sidebar/worktree-card-markdown-isolation.test.ts": 344, + "src/renderer/src/components/sidebar/worktree-card-pr-display.test.ts": 11, + "src/renderer/src/components/sidebar/worktree-card-send-target-inputs.test.ts": 7, + "src/renderer/src/components/sidebar/worktree-card-status-inputs.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-card-title-display.test.ts": 10, + "src/renderer/src/components/sidebar/worktree-context-menu-delete-intent.test.ts": 11, + "src/renderer/src/components/sidebar/worktree-delete-host-qualification.test.ts": 173, + "src/renderer/src/components/sidebar/worktree-delete-position-scaling.test.ts": 35, + "src/renderer/src/components/sidebar/worktree-delete-request.test.ts": 9, + "src/renderer/src/components/sidebar/worktree-drag-units.test.ts": 10, + "src/renderer/src/components/sidebar/worktree-filter-visibility.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-header-section-boundaries.test.ts": 6, + "src/renderer/src/components/sidebar/worktree-keyboard-cycle.test.ts": 7, + "src/renderer/src/components/sidebar/worktree-lineage-drag-drop.test.ts": 15, + "src/renderer/src/components/sidebar/worktree-lineage-expansion.performance.test.ts": 159, + "src/renderer/src/components/sidebar/worktree-lineage-projection.test.ts": 10, + "src/renderer/src/components/sidebar/worktree-lineage-toggle-handler-cache.test.ts": 10, + "src/renderer/src/components/sidebar/worktree-list-groups-host-collision.test.ts": 17, + "src/renderer/src/components/sidebar/worktree-list-groups-host-labels.test.ts": 23, + "src/renderer/src/components/sidebar/worktree-list-groups-imported-worktrees.test.ts": 22, + "src/renderer/src/components/sidebar/worktree-list-groups-lineage-nesting.test.ts": 17, + "src/renderer/src/components/sidebar/worktree-list-groups-nested-project-groups.test.ts": 16, + "src/renderer/src/components/sidebar/worktree-list-groups-notice-host-labels.test.ts": 16, + "src/renderer/src/components/sidebar/worktree-list-groups-pending-creations.test.ts": 13, + "src/renderer/src/components/sidebar/worktree-list-groups-pinned-host-labels.test.ts": 11, + "src/renderer/src/components/sidebar/worktree-list-groups-project-groups.test.ts": 16, + "src/renderer/src/components/sidebar/worktree-list-groups-project-host-setups.test.ts": 19, + "src/renderer/src/components/sidebar/worktree-list-groups-project-order.test.ts": 1284, + "src/renderer/src/components/sidebar/worktree-list-groups-section-label-disambiguation.test.ts": 12, + "src/renderer/src/components/sidebar/worktree-list/grouping/build-rows.folder-workspace-lanes.test.ts": 22, + "src/renderer/src/components/sidebar/worktree-list/grouping/build-rows.test.ts": 21, + "src/renderer/src/components/sidebar/worktree-list/grouping/host-labels.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-list/grouping/imported-rows.test.ts": 9, + "src/renderer/src/components/sidebar/worktree-list/listing/host-filtering.test.ts": 9, + "src/renderer/src/components/sidebar/worktree-list/listing/pending-worktree-creation-keys.test.ts": 5, + "src/renderer/src/components/sidebar/worktree-list/listing/review-cache-inputs.test.ts": 6, + "src/renderer/src/components/sidebar/worktree-list/listing/use-visible-worktrees.test.tsx": 35, + "src/renderer/src/components/sidebar/worktree-list/navigation/active-descendant-option.test.ts": 5, + "src/renderer/src/components/sidebar/worktree-list/navigation/folder-reveal.test.ts": 9, + "src/renderer/src/components/sidebar/worktree-list/navigation/render-row-lookup.folder-workspace.test.ts": 5, + "src/renderer/src/components/sidebar/worktree-list/navigation/use-keyboard.host-identity.test.tsx": 24, + "src/renderer/src/components/sidebar/worktree-list/navigation/use-reveal-requests.test.tsx": 124, + "src/renderer/src/components/sidebar/worktree-list/navigation/use-selection-host-collision.test.tsx": 18, + "src/renderer/src/components/sidebar/worktree-list/rows/FolderPathStatusIndicator.test.tsx": 38, + "src/renderer/src/components/sidebar/worktree-list/rows/RepoScanUnavailableIndicator.test.tsx": 57, + "src/renderer/src/components/sidebar/worktree-list/rows/indentation.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-list/rows/option-dom-host-collision.test.ts": 11, + "src/renderer/src/components/sidebar/worktree-list/rows/use-project-group-dialogs-owner-host.test.tsx": 39, + "src/renderer/src/components/sidebar/worktree-list/viewport/hard-scroll-up.test.ts": 14, + "src/renderer/src/components/sidebar/worktree-list/viewport/scroll-adjustment.test.ts": 15, + "src/renderer/src/components/sidebar/worktree-list/viewport/sticky-headers.test.ts": 26, + "src/renderer/src/components/sidebar/worktree-list/viewport/use-row-removal-animation.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-to-top.test.ts": 45, + "src/renderer/src/components/sidebar/worktree-list/viewport/use-scroll-to-top.test.tsx": 21, + "src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-list/viewport/visible-refresh.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-manual-order-catalog.test.ts": 16, + "src/renderer/src/components/sidebar/worktree-manual-order-store-write.test.ts": 22, + "src/renderer/src/components/sidebar/worktree-manual-order.test.ts": 315, + "src/renderer/src/components/sidebar/worktree-meta-updates.test.ts": 14, + "src/renderer/src/components/sidebar/worktree-multi-selection.test.ts": 8, + "src/renderer/src/components/sidebar/worktree-name-suggestions.test.ts": 73, + "src/renderer/src/components/sidebar/worktree-parent-eligibility.test.ts": 9, + "src/renderer/src/components/sidebar/worktree-review-helpers.test.tsx": 22, + "src/renderer/src/components/sidebar/worktree-scroll-to-current-button.test.ts": 4, + "src/renderer/src/components/sidebar/worktree-sidebar-drag-autoscroll.test.ts": 15, + "src/renderer/src/components/sidebar/worktree-sidebar-drag-geometry.test.ts": 27, + "src/renderer/src/components/sidebar/worktree-sidebar-drop-preview.test.ts": 13, + "src/renderer/src/components/sidebar/worktree-sidebar-pointer-drag-dom.test.ts": 7, + "src/renderer/src/components/sidebar/worktree-sidebar-reveal-scroll-settle.test.ts": 3, + "src/renderer/src/components/sidebar/worktree-sidebar-reveal.test.ts": 10, + "src/renderer/src/components/sidebar/worktree-sidebar-row-preference.test.ts": 7, + "src/renderer/src/components/sidebar/worktree-sort-label-ordering.test.ts": 180, + "src/renderer/src/components/sidebar/worktree-title-derived-agent-rows.test.ts": 20, + "src/renderer/src/components/sidebar/worktree-unambiguous-id-index.test.ts": 5, + "src/renderer/src/components/sidebar/worktree-unnest.test.ts": 19, + "src/renderer/src/components/sidebar/worktree-visibility-source-provenance.test.ts": 9, + "src/renderer/src/components/skills/SkillFreshnessNudge.test.tsx": 53, + "src/renderer/src/components/skills/SkillFreshnessStatusPill.test.tsx": 51, + "src/renderer/src/components/skills/SkillFreshnessUpdateDialog.test.tsx": 420, + "src/renderer/src/components/skills/SkillInstallAgentPicker.test.tsx": 226, + "src/renderer/src/components/skills/SkillInstallDialog.test.tsx": 1326, + "src/renderer/src/components/skills/SkillInstallManagementDialog.test.tsx": 1515, + "src/renderer/src/components/skills/SkillInstallTargetFields.test.tsx": 169, + "src/renderer/src/components/skills/SkillInstallWorkspaceCombobox.test.tsx": 537, + "src/renderer/src/components/skills/SkillShareDialog.test.tsx": 649, + "src/renderer/src/components/skills/SkillSharedLinkRow.test.tsx": 388, + "src/renderer/src/components/skills/SkillsPage.test.tsx": 1126, + "src/renderer/src/components/skills/skill-bundle-name.test.ts": 8, + "src/renderer/src/components/skills/skill-bundle-retry-selection.test.ts": 6, + "src/renderer/src/components/skills/skill-delete-copy.test.ts": 23, + "src/renderer/src/components/skills/skill-delete-selection.test.ts": 10, + "src/renderer/src/components/skills/skill-description-length.test.ts": 4, + "src/renderer/src/components/skills/skill-freshness-grouping.test.ts": 18, + "src/renderer/src/components/skills/skill-freshness-skipped-reason.test.ts": 11, + "src/renderer/src/components/skills/skill-install-progress-state.test.tsx": 23, + "src/renderer/src/components/skills/skill-install-provider-groups.test.ts": 11, + "src/renderer/src/components/skills/skill-install-workspace-choices.test.ts": 8, + "src/renderer/src/components/skills/skill-package-checklist-items.test.ts": 12, + "src/renderer/src/components/skills/skill-package-digest.test.ts": 5, + "src/renderer/src/components/skills/skill-package-install-risk.test.ts": 8, + "src/renderer/src/components/skills/skill-share-link.test.ts": 4, + "src/renderer/src/components/skills/skill-share-package-selection.test.ts": 5, + "src/renderer/src/components/skills/skill-share-preview-summary.test.ts": 21, + "src/renderer/src/components/skills/skill-share-selection.test.ts": 6, + "src/renderer/src/components/skills/skill-source-inventory.test.ts": 8, + "src/renderer/src/components/skills/skills-filter.test.ts": 5, + "src/renderer/src/components/source-control/SourceControlActionVariableChips.test.tsx": 56, + "src/renderer/src/components/star-nag/StarNagAgentValueMomentObserver.test.tsx": 86, + "src/renderer/src/components/star-nag/StarNagToastHost.test.tsx": 130, + "src/renderer/src/components/stats/GrokUsagePane.test.tsx": 106, + "src/renderer/src/components/stats/UsageBreakdownSection.test.tsx": 35, + "src/renderer/src/components/stats/UsageTrackingPaneShell.test.tsx": 230, + "src/renderer/src/components/stats/usage-daily-chart.test.tsx": 300, + "src/renderer/src/components/stats/usage-overview-model.test.ts": 202, + "src/renderer/src/components/status-bar/CaffeinateStatusSegment.localization.test.tsx": 645, + "src/renderer/src/components/status-bar/PetStatusSegment.layout.test.ts": 4, + "src/renderer/src/components/status-bar/PortsStatusSegment.host-routing.test.tsx": 100, + "src/renderer/src/components/status-bar/PortsStatusSegment.render-stability.test.tsx": 36, + "src/renderer/src/components/status-bar/ResourceUsageStatusSegment.rows.test.tsx": 62, + "src/renderer/src/components/status-bar/ResourceUsageStatusSegment.session-polling.test.ts": 6, + "src/renderer/src/components/status-bar/RuntimeHostStatusRow.test.tsx": 130, + "src/renderer/src/components/status-bar/SkillUpdateStatusSegment.test.tsx": 64, + "src/renderer/src/components/status-bar/SshStatusSegment.test.ts": 10, + "src/renderer/src/components/status-bar/UsagePercentageDisplayChangeNotice.test.tsx": 137, + "src/renderer/src/components/status-bar/UsageRosterPanel.test.tsx": 102, + "src/renderer/src/components/status-bar/codex-restart-status-summary.test.ts": 6, + "src/renderer/src/components/status-bar/codex-status-sign-in.test.tsx": 830, + "src/renderer/src/components/status-bar/icons.test.tsx": 16, + "src/renderer/src/components/status-bar/inline-usage-bars.test.tsx": 742, + "src/renderer/src/components/status-bar/mergeSnapshotAndSessions.test.ts": 29, + "src/renderer/src/components/status-bar/ports-status-popover-rows.test.tsx": 50, + "src/renderer/src/components/status-bar/provider-account-sync-key.test.ts": 6, + "src/renderer/src/components/status-bar/provider-segment-monthly-window.test.tsx": 672, + "src/renderer/src/components/status-bar/remote-host-connection-status.test.ts": 10, + "src/renderer/src/components/status-bar/resource-manager-terminal-copy.test.ts": 7, + "src/renderer/src/components/status-bar/resource-manager-worktree-target.test.ts": 5, + "src/renderer/src/components/status-bar/resource-memory-metric-copy.test.ts": 7, + "src/renderer/src/components/status-bar/resource-session-bindings.test.ts": 8, + "src/renderer/src/components/status-bar/resource-session-classification-parity.test.ts": 3, + "src/renderer/src/components/status-bar/resource-session-inventory.test.ts": 9, + "src/renderer/src/components/status-bar/resource-session-kill-confirmation.test.ts": 5, + "src/renderer/src/components/status-bar/resource-session-navigation.test.ts": 10, + "src/renderer/src/components/status-bar/resource-usage-open-slices.test.ts": 10, + "src/renderer/src/components/status-bar/resource-usage-space-scan-ready.test.ts": 7, + "src/renderer/src/components/status-bar/ssh-status-segment-copy.test.ts": 5, + "src/renderer/src/components/status-bar/status-bar-agent-gating.test.ts": 6, + "src/renderer/src/components/status-bar/status-bar-context-menu-policy.test.ts": 4, + "src/renderer/src/components/status-bar/status-bar-copy-localization.test.tsx": 310, + "src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx": 773, + "src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts": 12, + "src/renderer/src/components/status-bar/status-bar-runtime-groups.test.ts": 12, + "src/renderer/src/components/status-bar/tooltip.test.ts": 51, + "src/renderer/src/components/status-bar/usage-error-copy.test.ts": 5, + "src/renderer/src/components/status-bar/usage-percentage-label.test.ts": 4, + "src/renderer/src/components/status-bar/usage-provider-settings-target.test.ts": 6, + "src/renderer/src/components/status-bar/usage-roster-formatting.test.ts": 6, + "src/renderer/src/components/status-bar/usage-roster-row-state.test.ts": 6, + "src/renderer/src/components/status-bar/use-resource-session-inventory.test.tsx": 677, + "src/renderer/src/components/status-bar/workspace-space-breakdown-list.test.tsx": 69, + "src/renderer/src/components/status-bar/workspace-space-delete-host-routing.test.ts": 8, + "src/renderer/src/components/status-bar/workspace-space-format.test.ts": 8, + "src/renderer/src/components/status-bar/workspace-space-layout.test.ts": 5, + "src/renderer/src/components/status-bar/workspace-space-manager-source-boundary.test.ts": 8, + "src/renderer/src/components/status-bar/workspace-space-presentation.test.ts": 133, + "src/renderer/src/components/tab-bar/BrowserTab.test.tsx": 1412, + "src/renderer/src/components/tab-bar/ClientHostedBrowserTabRows.test.tsx": 106, + "src/renderer/src/components/tab-bar/EditorFileTab.test.tsx": 840, + "src/renderer/src/components/tab-bar/EditorFileTabContextMenu.test.tsx": 505, + "src/renderer/src/components/tab-bar/QuickLaunchButton.test.ts": 37, + "src/renderer/src/components/tab-bar/RecentTabSwitcher.test.tsx": 46, + "src/renderer/src/components/tab-bar/SortableTab.rename-shortcut.test.tsx": 739, + "src/renderer/src/components/tab-bar/SortableTab.update-depth-probe.test.tsx": 113, + "src/renderer/src/components/tab-bar/SortableTabContextMenu.test.tsx": 121, + "src/renderer/src/components/tab-bar/TabBar.client-hosted-row-active-state.test.ts": 1116, + "src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts": 2180, + "src/renderer/src/components/tab-bar/TabBar.windows-shell-launch.test.ts": 2043, + "src/renderer/src/components/tab-bar/TabBar.windows-shell-remote-runtime.test.ts": 1451, + "src/renderer/src/components/tab-bar/TabBar.windows-shell-ssh-host.test.ts": 1914, + "src/renderer/src/components/tab-bar/TabBar.worktree-write-gate.test.tsx": 1048, + "src/renderer/src/components/tab-bar/TabBar.worktree-write-gate.windows.test.tsx": 477, + "src/renderer/src/components/tab-bar/TabBarCreateEntry.history.test.tsx": 100, + "src/renderer/src/components/tab-bar/TabBarCreateEntry.keyboard.test.tsx": 166, + "src/renderer/src/components/tab-bar/TabBarCreateEntry.search.test.tsx": 199, + "src/renderer/src/components/tab-bar/TabBarCreateEntry.tab-results.test.tsx": 171, + "src/renderer/src/components/tab-bar/TabBarCreateEntryRow.test.tsx": 102, + "src/renderer/src/components/tab-bar/TabBarQuickCommandItem.test.tsx": 106, + "src/renderer/src/components/tab-bar/TabBarQuickCommandsMenu.keyboard.test.ts": 134, + "src/renderer/src/components/tab-bar/TabStripScrollIndicator.test.tsx": 63, + "src/renderer/src/components/tab-bar/TerminalTabLeadingIcon.test.tsx": 36, + "src/renderer/src/components/tab-bar/client-hosted-browser-row-strip-placement.test.ts": 6, + "src/renderer/src/components/tab-bar/drop-indicator.test.ts": 8, + "src/renderer/src/components/tab-bar/editor-tab-local-open-guard.test.ts": 5, + "src/renderer/src/components/tab-bar/group-tab-order.test.ts": 8, + "src/renderer/src/components/tab-bar/middle-button-default-guard.test.ts": 5, + "src/renderer/src/components/tab-bar/native-chat-tab-agent-evidence.test.ts": 9, + "src/renderer/src/components/tab-bar/open-tab-entry-dedupe.test.ts": 8, + "src/renderer/src/components/tab-bar/open-tab-search-retention.test.ts": 20, + "src/renderer/src/components/tab-bar/open-tab-search.test.ts": 35, + "src/renderer/src/components/tab-bar/open-tab-selection-routing.test.ts": 12, + "src/renderer/src/components/tab-bar/query-token-match.test.ts": 7, + "src/renderer/src/components/tab-bar/recent-tab-switching.test.ts": 8, + "src/renderer/src/components/tab-bar/reconcile-order.test.ts": 6, + "src/renderer/src/components/tab-bar/tab-agent-launch-options.test.ts": 15, + "src/renderer/src/components/tab-bar/tab-agent-types-by-tab-id.test.ts": 13, + "src/renderer/src/components/tab-bar/tab-bar-item-surface.client-hosted-active-state.test.tsx": 14, + "src/renderer/src/components/tab-bar/tab-context-menu-consistency.test.tsx": 8, + "src/renderer/src/components/tab-bar/tab-create-entry-action.test.ts": 35, + "src/renderer/src/components/tab-bar/tab-create-entry-classifier.test.ts": 50, + "src/renderer/src/components/tab-bar/tab-create-entry-file-matches.test.ts": 36, + "src/renderer/src/components/tab-bar/tab-create-entry-forced-search.test.ts": 4, + "src/renderer/src/components/tab-bar/tab-create-entry-history-placement.test.ts": 6, + "src/renderer/src/components/tab-bar/tab-create-entry-local-path.test.ts": 285, + "src/renderer/src/components/tab-bar/tab-create-menu-options.test.ts": 16, + "src/renderer/src/components/tab-bar/tab-move-to-pane-column.test.ts": 12, + "src/renderer/src/components/tab-bar/tab-strip-content-resize-observers.test.ts": 11, + "src/renderer/src/components/tab-bar/tab-strip-overflow-navigation.test.ts": 6, + "src/renderer/src/components/tab-bar/tab-strip-pointer-activation.test.tsx": 35, + "src/renderer/src/components/tab-bar/tab-strip-scroll-metrics.test.ts": 6, + "src/renderer/src/components/tab-bar/tab-title-tooltip.test.tsx": 28, + "src/renderer/src/components/tab-bar/terminal-tab-activity-status.test.ts": 46, + "src/renderer/src/components/tab-bar/terminal-tab-spinner-launch-agent.test.ts": 15, + "src/renderer/src/components/tab-bar/use-open-tab-search.test.ts": 221, + "src/renderer/src/components/tab-bar/use-tab-bar-quick-command-search-input.test.ts": 29, + "src/renderer/src/components/tab-bar/windows-shell-launch.test.ts": 5, + "src/renderer/src/components/tab-bar/windows-shell-menu-visibility.test.ts": 7, + "src/renderer/src/components/tab-group/RetainedPaneHost.test.tsx": 75, + "src/renderer/src/components/tab-group/TabGroupPanel.context-menu.test.ts": 7, + "src/renderer/src/components/tab-group/TabGroupSplitLayout.drag.test.tsx": 128, + "src/renderer/src/components/tab-group/TabGroupSplitLayout.test.ts": 10, + "src/renderer/src/components/tab-group/TabPaneColumnSplitDragOverlay.test.tsx": 7, + "src/renderer/src/components/tab-group/tab-drag-pointer.test.ts": 4, + "src/renderer/src/components/tab-group/tab-drag-preview-activation.test.ts": 10, + "src/renderer/src/components/tab-group/tab-drag-retained-guest-passthrough.test.tsx": 60, + "src/renderer/src/components/tab-group/tab-drop-zone.test.ts": 4, + "src/renderer/src/components/tab-group/tab-group-body-anchor.test.ts": 4, + "src/renderer/src/components/tab-group/tab-group-panel-split-target.test.ts": 18, + "src/renderer/src/components/tab-group/tab-insertion.test.ts": 7, + "src/renderer/src/components/tab-group/useTabDragSplit.test.ts": 78, + "src/renderer/src/components/tab-group/useTabGroupCreationCommands.local-shell.test.ts": 679, + "src/renderer/src/components/tab-group/useTabGroupTabCloseCommands.structured-session.test.ts": 269, + "src/renderer/src/components/tab-group/useTabGroupTabCloseCommands.test.tsx": 69, + "src/renderer/src/components/tab-group/useTabGroupWorkspaceModel.focus.test.ts": 1235, + "src/renderer/src/components/task-drawer-source-boundary.test.ts": 8, + "src/renderer/src/components/task-page-cache-selectors.test.ts": 11, + "src/renderer/src/components/task-page-checks-pill.test.ts": 5, + "src/renderer/src/components/task-page-default-repo-selection.test.ts": 18, + "src/renderer/src/components/task-page-empty-state.test.ts": 14, + "src/renderer/src/components/task-page-github-dialog-state-authority.test.ts": 10, + "src/renderer/src/components/task-page-github-issue-creation.test.ts": 5, + "src/renderer/src/components/task-page-github-list-scroll-restore.test.ts": 20, + "src/renderer/src/components/task-page-github-resume-cache.test.ts": 17, + "src/renderer/src/components/task-page-github-reviewer-suggestions.test.ts": 6, + "src/renderer/src/components/task-page-github-status-actions.test.ts": 7, + "src/renderer/src/components/task-page-github-status-state.test.ts": 7, + "src/renderer/src/components/task-page-github-task-kind.test.ts": 15, + "src/renderer/src/components/task-page-github-work-item-filter-membership.test.ts": 10, + "src/renderer/src/components/task-page-github-work-item-mutation-patches.test.ts": 13, + "src/renderer/src/components/task-page-github-work-item-mutation-regressions.test.ts": 22, + "src/renderer/src/components/task-page-github-work-item-mutations.test.ts": 24, + "src/renderer/src/components/task-page-github-work-item-status.test.ts": 9, + "src/renderer/src/components/task-page-gitlab-task-filters.test.ts": 5, + "src/renderer/src/components/task-page-initial-selection-scaling.test.tsx": 41, + "src/renderer/src/components/task-page-jira-cache-selectors.test.ts": 7, + "src/renderer/src/components/task-page-jira-create-fields.test.ts": 9, + "src/renderer/src/components/task-page-jira-grouping.test.ts": 194, + "src/renderer/src/components/task-page-jira-item-source-context.test.ts": 7, + "src/renderer/src/components/task-page-jira-load-state.test.ts": 7, + "src/renderer/src/components/task-page-jira-project-selection.test.ts": 8, + "src/renderer/src/components/task-page-jira-sort-controls.test.tsx": 186, + "src/renderer/src/components/task-page-jira-sorting.test.ts": 151, + "src/renderer/src/components/task-page-linear-in-orca-issues.test.ts": 14, + "src/renderer/src/components/task-page-linear-issue-dialog-popover-scroll.test.ts": 9, + "src/renderer/src/components/task-page-linear-issue-empty-state.test.ts": 6, + "src/renderer/src/components/task-page-linear-issue-grouping.test.ts": 11, + "src/renderer/src/components/task-page-linear-issue-request.test.ts": 6, + "src/renderer/src/components/task-page-linear-team-selection.test.ts": 9, + "src/renderer/src/components/task-page-list-chrome-visibility.test.ts": 7, + "src/renderer/src/components/task-page-localized-options.test.ts": 79, + "src/renderer/src/components/task-page-mutation-page-allocation.test.ts": 8, + "src/renderer/src/components/task-page-new-issue-draft.test.ts": 6, + "src/renderer/src/components/task-page-pagination-page-numbers.test.ts": 7, + "src/renderer/src/components/task-page-pr-check-summary.test.ts": 6, + "src/renderer/src/components/task-page-repo-source-context.test.ts": 10, + "src/renderer/src/components/task-page-repo-source-divergence.test.ts": 5, + "src/renderer/src/components/task-page-source-switch-boundary.test.ts": 9, + "src/renderer/src/components/task-page-task-creation-drafts.test.ts": 10, + "src/renderer/src/components/task-page-task-source-host-availability.test.ts": 11, + "src/renderer/src/components/task-page-work-item-pagination.test.ts": 18, + "src/renderer/src/components/task-page-workspace-composer-boundary.test.ts": 223, + "src/renderer/src/components/task-source-context-summary.test.ts": 17, + "src/renderer/src/components/task-source-provider-availability.test.ts": 10, + "src/renderer/src/components/terminal-pane/CloseTerminalDialog.test.tsx": 135, + "src/renderer/src/components/terminal-pane/MobileDriverOverlay.test.tsx": 12, + "src/renderer/src/components/terminal-pane/PinnedTabCloseDialog.test.tsx": 200, + "src/renderer/src/components/terminal-pane/RunningTerminalCloseDialog.test.tsx": 269, + "src/renderer/src/components/terminal-pane/SessionRestoredBanner.test.tsx": 33, + "src/renderer/src/components/terminal-pane/TerminalAgentSessionForkDialog.test.tsx": 207, + "src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx": 58, + "src/renderer/src/components/terminal-pane/TerminalErrorToast.test.ts": 127, + "src/renderer/src/components/terminal-pane/TerminalPaneHeaderOverlay.test.tsx": 101, + "src/renderer/src/components/terminal-pane/TerminalPaneNativeChatPortal.test.tsx": 24, + "src/renderer/src/components/terminal-pane/TerminalPaneOverlayLayer.react185.test.tsx": 60, + "src/renderer/src/components/terminal-pane/TerminalProcessExitOverlay.test.tsx": 76, + "src/renderer/src/components/terminal-pane/TerminalRemoteRuntimeReconnectBanner.test.tsx": 70, + "src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.test.tsx": 192, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-attention-dispatch.test.ts": 39, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-completion-replay-guard.test.ts": 20, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-dispose-leak.test.ts": 13, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-hook-done-quiet-window.test.ts": 28, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-hook-title-precedence.test.ts": 34, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-monitoring-turn-end.test.ts": 19, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-pending-title-inspection.test.ts": 18, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-process-cadence.test.ts": 44, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-queued-inspection-disposal.test.ts": 24, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-stamped-turn-boundary.test.ts": 20, + "src/renderer/src/components/terminal-pane/agent-completion-coordinator-stamped-turn-replay.test.ts": 23, + "src/renderer/src/components/terminal-pane/agent-completion-no-evidence-cadence.test.ts": 47, + "src/renderer/src/components/terminal-pane/agent-completion-poll-interval.test.ts": 185, + "src/renderer/src/components/terminal-pane/agent-completion-stale-evidence-backoff.test.ts": 6, + "src/renderer/src/components/terminal-pane/agent-completion-steady-state-opt-in.test.ts": 11, + "src/renderer/src/components/terminal-pane/agent-hook-terminal-lifecycle.test.ts": 11, + "src/renderer/src/components/terminal-pane/agent-interrupt-inference.test.ts": 30, + "src/renderer/src/components/terminal-pane/agent-process-inspection-queue-rejection-containment.test.ts": 14, + "src/renderer/src/components/terminal-pane/agent-process-inspection-round.test.ts": 18, + "src/renderer/src/components/terminal-pane/agent-question-answered-inference.test.ts": 14, + "src/renderer/src/components/terminal-pane/cache-timer-seeding.test.ts": 8, + "src/renderer/src/components/terminal-pane/codex-auto-approval-notification-suppression.test.ts": 112, + "src/renderer/src/components/terminal-pane/codex-backfill-error-detector.test.ts": 5, + "src/renderer/src/components/terminal-pane/codex-detached-pane-restart.test.ts": 305, + "src/renderer/src/components/terminal-pane/command-code-done-settle.test.ts": 18, + "src/renderer/src/components/terminal-pane/command-code-output-ownership.test.ts": 8, + "src/renderer/src/components/terminal-pane/compose-active-terminal-theme.test.ts": 7, + "src/renderer/src/components/terminal-pane/deferred-reattach-live-data-queue.test.ts": 173, + "src/renderer/src/components/terminal-pane/deferred-split-pane-handoff.test.ts": 15, + "src/renderer/src/components/terminal-pane/desktop-fit-fallback.test.ts": 9, + "src/renderer/src/components/terminal-pane/direct-ssh-hidden-output-restore-unavailable-banner.test.ts": 1045, + "src/renderer/src/components/terminal-pane/edge-wrapped-terminal-http-links.test.ts": 31, + "src/renderer/src/components/terminal-pane/expand-collapse-render-stability.test.ts": 26, + "src/renderer/src/components/terminal-pane/expand-collapse.test.ts": 8, + "src/renderer/src/components/terminal-pane/focus-terminal-pane-event.test.ts": 10, + "src/renderer/src/components/terminal-pane/focused-pane-rim-flash.test.ts": 7, + "src/renderer/src/components/terminal-pane/force-park-buffer-capture.test.ts": 5, + "src/renderer/src/components/terminal-pane/git-bash-console-capacity.test.ts": 12, + "src/renderer/src/components/terminal-pane/hard-wrapped-terminal-http-links.test.ts": 34, + "src/renderer/src/components/terminal-pane/hidden-output-restore-scheduler.test.ts": 16, + "src/renderer/src/components/terminal-pane/hidden-reveal-reconciliation.fuzz.test.ts": 7500, + "src/renderer/src/components/terminal-pane/issue-12112-agent-pane-startup-color-reply-leak.repro.test.ts": 21, + "src/renderer/src/components/terminal-pane/issue-4631-terminal-hover-link-provider.repro.test.ts": 177, + "src/renderer/src/components/terminal-pane/keyboard-handlers-ime-composing-chord.test.tsx": 48, + "src/renderer/src/components/terminal-pane/keyboard-handlers-ime-enter-keyup.test.tsx": 59, + "src/renderer/src/components/terminal-pane/keyboard-handlers-ime.test.tsx": 54, + "src/renderer/src/components/terminal-pane/keyboard-handlers.test.ts": 17, + "src/renderer/src/components/terminal-pane/layout-serialization.test.ts": 19, + "src/renderer/src/components/terminal-pane/manual-terminal-worktree-park-eligibility.test.ts": 8, + "src/renderer/src/components/terminal-pane/merge-captured-leaf-state.test.ts": 16, + "src/renderer/src/components/terminal-pane/mobile-driver-overlay-collapse.test.ts": 7, + "src/renderer/src/components/terminal-pane/mobile-driver-overlay-focus.test.ts": 5, + "src/renderer/src/components/terminal-pane/mobile-driver-overlay-visibility.test.ts": 6, + "src/renderer/src/components/terminal-pane/mouse-hide-while-typing.test.ts": 6, + "src/renderer/src/components/terminal-pane/native-chat-leaf-title-agent.test.ts": 13, + "src/renderer/src/components/terminal-pane/osc52-clipboard-default-on-notice.test.ts": 58, + "src/renderer/src/components/terminal-pane/osc52-clipboard-toast.test.ts": 69, + "src/renderer/src/components/terminal-pane/osc52-clipboard.test.ts": 122, + "src/renderer/src/components/terminal-pane/override-affected-panes.test.ts": 7, + "src/renderer/src/components/terminal-pane/paired-reconnect-multi-pane-materialization.test.ts": 1727, + "src/renderer/src/components/terminal-pane/pane-agent-session-id.test.ts": 5, + "src/renderer/src/components/terminal-pane/pane-foreground-agent-tracker.test.ts": 59, + "src/renderer/src/components/terminal-pane/pane-foreground-inspect-observation-identity.test.ts": 10, + "src/renderer/src/components/terminal-pane/pane-helpers.test.ts": 9, + "src/renderer/src/components/terminal-pane/pane-title-overlay-rects.test.ts": 6, + "src/renderer/src/components/terminal-pane/parked-terminal-byte-watcher.test.ts": 2029, + "src/renderer/src/components/terminal-pane/parked-terminal-command-status.test.ts": 56, + "src/renderer/src/components/terminal-pane/parse-osc7.test.ts": 5, + "src/renderer/src/components/terminal-pane/pty-buffer-serializer.test.ts": 19, + "src/renderer/src/components/terminal-pane/pty-connection-agent-session-resume.test.ts": 1727, + "src/renderer/src/components/terminal-pane/pty-connection-cold-restore-agent-resume.test.ts": 1162, + "src/renderer/src/components/terminal-pane/pty-connection-cold-restore-repaint.test.ts": 699, + "src/renderer/src/components/terminal-pane/pty-connection-cold-restore-resume-command.test.ts": 709, + "src/renderer/src/components/terminal-pane/pty-connection-command-finished-cleanup.test.ts": 1591, + "src/renderer/src/components/terminal-pane/pty-connection-daemon-snapshot-replay.test.ts": 3135, + "src/renderer/src/components/terminal-pane/pty-connection-deferred-reattach-live-output.test.ts": 1466, + "src/renderer/src/components/terminal-pane/pty-connection-deferred-ssh-passphrase.test.ts": 1225, + "src/renderer/src/components/terminal-pane/pty-connection-deliberate-sleep-guard.test.ts": 1131, + "src/renderer/src/components/terminal-pane/pty-connection-direct-ssh-reattach-retry.test.ts": 1727, + "src/renderer/src/components/terminal-pane/pty-connection-direct-ssh-spawn-retry.test.ts": 1570, + "src/renderer/src/components/terminal-pane/pty-connection-foreground-agent-routing.test.ts": 1216, + "src/renderer/src/components/terminal-pane/pty-connection-foreground-agent-sampling.test.ts": 1659, + "src/renderer/src/components/terminal-pane/pty-connection-foreground-routing-confirmation.test.ts": 624, + "src/renderer/src/components/terminal-pane/pty-connection-foreground-write-path.test.ts": 1254, + "src/renderer/src/components/terminal-pane/pty-connection-fresh-spawn-guards.test.ts": 1432, + "src/renderer/src/components/terminal-pane/pty-connection-hibernation-wake.test.ts": 1373, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-atlas-recovery.test.ts": 1032, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-backlog-reconciliation.test.ts": 939, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-backlog-snapshot.test.ts": 3190, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-codex-queries.test.ts": 1467, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-delivery-gate.test.ts": 1677, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-output-restore.test.ts": 597, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-query-snapshot-restore.test.ts": 1298, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-restore-fit-overflow.test.ts": 589, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-snapshot-live-overlap.test.ts": 814, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-snapshot-resize-signals.test.ts": 1012, + "src/renderer/src/components/terminal-pane/pty-connection-hidden-tui-snapshot-replay.test.ts": 835, + "src/renderer/src/components/terminal-pane/pty-connection-hook-completion-bell-arbitration.test.ts": 1536, + "src/renderer/src/components/terminal-pane/pty-connection-hook-completion-side-effects.test.ts": 1433, + "src/renderer/src/components/terminal-pane/pty-connection-hook-idle-arbitration.test.ts": 1039, + "src/renderer/src/components/terminal-pane/pty-connection-interrupt-inference.test.ts": 916, + "src/renderer/src/components/terminal-pane/pty-connection-main-side-effect-authority.test.ts": 922, + "src/renderer/src/components/terminal-pane/pty-connection-mode-2031-subscriptions.test.ts": 1134, + "src/renderer/src/components/terminal-pane/pty-connection-notification-settings-gating.test.ts": 869, + "src/renderer/src/components/terminal-pane/pty-connection-parked-ssh-snapshot.test.ts": 1744, + "src/renderer/src/components/terminal-pane/pty-connection-post-dispose-restore-termination.test.ts": 771, + "src/renderer/src/components/terminal-pane/pty-connection-pty-exit-teardown.test.ts": 3670, + "src/renderer/src/components/terminal-pane/pty-connection-queued-startup-consume.test.ts": 705, + "src/renderer/src/components/terminal-pane/pty-connection-reattach-binding.test.ts": 2385, + "src/renderer/src/components/terminal-pane/pty-connection-reattach-mode-reset.test.ts": 1987, + "src/renderer/src/components/terminal-pane/pty-connection-remote-runtime-attach.test.ts": 1224, + "src/renderer/src/components/terminal-pane/pty-connection-remote-snapshot-source-grid.test.ts": 807, + "src/renderer/src/components/terminal-pane/pty-connection-renderer-risk-repaint.test.ts": 1489, + "src/renderer/src/components/terminal-pane/pty-connection-replay-payload-handling.test.ts": 1203, + "src/renderer/src/components/terminal-pane/pty-connection-restored-baseline-shortfall.test.ts": 835, + "src/renderer/src/components/terminal-pane/pty-connection-runtime-owner-spawn-routing.test.ts": 925, + "src/renderer/src/components/terminal-pane/pty-connection-session-liveness.test.ts": 1912, + "src/renderer/src/components/terminal-pane/pty-connection-setup-split-spawn.test.ts": 1083, + "src/renderer/src/components/terminal-pane/pty-connection-sleeping-resume-banner.test.ts": 798, + "src/renderer/src/components/terminal-pane/pty-connection-spawn-left-pane-unbound.test.ts": 897, + "src/renderer/src/components/terminal-pane/pty-connection-split-cwd-resolution.test.ts": 47, + "src/renderer/src/components/terminal-pane/pty-connection-ssh-startup-draft-delivery.test.ts": 698, + "src/renderer/src/components/terminal-pane/pty-connection-stalled-hidden-restore.test.ts": 3547, + "src/renderer/src/components/terminal-pane/pty-connection-startup-command-delivery.test.ts": 876, + "src/renderer/src/components/terminal-pane/pty-connection-task-complete-dispatch.test.ts": 1186, + "src/renderer/src/components/terminal-pane/pty-connection-terminal-input-gating.test.ts": 4079, + "src/renderer/src/components/terminal-pane/pty-connection-typed-agent-identity.test.ts": 1778, + "src/renderer/src/components/terminal-pane/pty-connection-visibility-resume-size.test.ts": 1757, + "src/renderer/src/components/terminal-pane/pty-connection-visible-pane-output-pause-latch.test.ts": 860, + "src/renderer/src/components/terminal-pane/pty-connection-windows-cjk-repaint.test.ts": 1118, + "src/renderer/src/components/terminal-pane/pty-connection-windows-keyboard-reset.test.ts": 1660, + "src/renderer/src/components/terminal-pane/pty-connection/direct-ssh-reattach-recovery.test.ts": 8, + "src/renderer/src/components/terminal-pane/pty-connection/foreground-output-budgets.test.ts": 4, + "src/renderer/src/components/terminal-pane/pty-connection/pane-pty-layout-binding.test.ts": 6, + "src/renderer/src/components/terminal-pane/pty-connection/reattach-payload-context.test.ts": 7, + "src/renderer/src/components/terminal-pane/pty-connection/reattach-payload-ssh-reconnect-model-paint.test.ts": 16, + "src/renderer/src/components/terminal-pane/pty-connection/reattach-success-settle.test.ts": 12, + "src/renderer/src/components/terminal-pane/pty-connection/ssh-session-gone-verdict.test.ts": 5, + "src/renderer/src/components/terminal-pane/pty-connection/terminal-tab-id.test.ts": 6, + "src/renderer/src/components/terminal-pane/pty-connection/unbound-pane-spawn-recovery.test.ts": 12, + "src/renderer/src/components/terminal-pane/pty-dispatcher-delivery-interest.test.ts": 98, + "src/renderer/src/components/terminal-pane/pty-dispatcher-delivery-resync.test.ts": 56, + "src/renderer/src/components/terminal-pane/pty-dispatcher-pi-routing.test.ts": 317, + "src/renderer/src/components/terminal-pane/pty-dispatcher-push-reattach.test.ts": 89, + "src/renderer/src/components/terminal-pane/pty-input-write-queue.test.ts": 750, + "src/renderer/src/components/terminal-pane/pty-model-restore-channel.test.ts": 10, + "src/renderer/src/components/terminal-pane/pty-pre-handler-buffer-warn-eviction.test.ts": 17, + "src/renderer/src/components/terminal-pane/pty-pre-handler-buffer.test.ts": 47, + "src/renderer/src/components/terminal-pane/pty-preconnect-input-buffer.test.ts": 50, + "src/renderer/src/components/terminal-pane/pty-renderer-delivery-claims.test.ts": 10, + "src/renderer/src/components/terminal-pane/pty-shutdown-data-suspension.test.ts": 10, + "src/renderer/src/components/terminal-pane/pty-shutdown-exit-deferral.test.ts": 68, + "src/renderer/src/components/terminal-pane/pty-shutdown-output-queue.test.ts": 1985, + "src/renderer/src/components/terminal-pane/pty-side-effect-pending-census.test.ts": 161, + "src/renderer/src/components/terminal-pane/pty-size-reassertion.test.ts": 12, + "src/renderer/src/components/terminal-pane/pty-size-reconcile.test.ts": 14, + "src/renderer/src/components/terminal-pane/pty-transport-connect-spawn.test.ts": 383, + "src/renderer/src/components/terminal-pane/pty-transport-detach-attach-handoff.test.ts": 238, + "src/renderer/src/components/terminal-pane/pty-transport-eager-buffer-replay.test.ts": 321, + "src/renderer/src/components/terminal-pane/pty-transport-handler-suspension.test.ts": 311, + "src/renderer/src/components/terminal-pane/pty-transport-input-write.test.ts": 759, + "src/renderer/src/components/terminal-pane/pty-transport-output-side-effects.test.ts": 310, + "src/renderer/src/components/terminal-pane/pty-transport-pi-coalesce.test.ts": 169, + "src/renderer/src/components/terminal-pane/pty-transport-pi-spinner.test.ts": 165, + "src/renderer/src/components/terminal-pane/pty-transport-reattach-admission.test.ts": 298, + "src/renderer/src/components/terminal-pane/pty-transport-recycled-pty-incarnation.test.ts": 157, + "src/renderer/src/components/terminal-pane/pty-transport-spawn-errors.test.ts": 148, + "src/renderer/src/components/terminal-pane/regular-terminal-focus-ownership.test.ts": 22, + "src/renderer/src/components/terminal-pane/remote-desktop-viewport-claim.test.ts": 5, + "src/renderer/src/components/terminal-pane/remote-execution-host-pty.test.ts": 5, + "src/renderer/src/components/terminal-pane/remote-hidden-output-restore-outcomes.test.ts": 1241, + "src/renderer/src/components/terminal-pane/remote-hidden-output-restore-unavailable-banner.repro.test.ts": 1012, + "src/renderer/src/components/terminal-pane/remote-pane-layout-push.test.ts": 10, + "src/renderer/src/components/terminal-pane/remote-runtime-connect-failure-recovery.test.ts": 1731, + "src/renderer/src/components/terminal-pane/remote-runtime-error-surface-dismissal.test.ts": 1369, + "src/renderer/src/components/terminal-pane/remote-runtime-outage-toast-flood-and-stuck-reconnect.test.ts": 2153, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-batching.test.ts": 72, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-deadline-reattach.test.ts": 2204, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-latched-pane-retention.test.ts": 4200, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-query-reply-immediate.test.ts": 2087, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-recovery-state.test.ts": 26, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-snapshot-escape-tail.test.ts": 1389, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-snapshot-source-grid.test.ts": 1605, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-activation-inventory-fallback.test.ts": 2053, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts": 3506, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-handoff.test.ts": 2653, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-outcome-recovery.test.ts": 2166, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-end-verdict.test.ts": 1292, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-expired-pane-recovery.test.ts": 2463, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-host-session-launch.test.ts": 2411, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-host-surface-replacement.test.ts": 5095, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-input-coalescing.test.ts": 3404, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-input-fallback.test.ts": 2595, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-pane-handle-resolution.test.ts": 2613, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-pending-host-surface-attach.test.ts": 1727, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-snapshot-replay.test.ts": 1654, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-split-leaf-activation.test.ts": 2871, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-stale-handle-recovery.test.ts": 2313, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-sticky-replacement-policy.test.ts": 2038, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-stream-reconnect.test.ts": 3273, + "src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-web-mirror-recovery.test.ts": 2711, + "src/renderer/src/components/terminal-pane/remote-runtime-resubscribe-failure-recovery-routing.test.ts": 1695, + "src/renderer/src/components/terminal-pane/renderer-owned-agent-status-registry.test.ts": 8, + "src/renderer/src/components/terminal-pane/replay-guard.test.ts": 37, + "src/renderer/src/components/terminal-pane/replayed-scrollback-store-release.test.ts": 5, + "src/renderer/src/components/terminal-pane/repro-8299-shift-space-input-source.test.ts": 14, + "src/renderer/src/components/terminal-pane/repro-8832-url-next-line.test.ts": 19, + "src/renderer/src/components/terminal-pane/resolve-split-cwd.test.ts": 13, + "src/renderer/src/components/terminal-pane/restored-snapshot-coverage.test.ts": 5, + "src/renderer/src/components/terminal-pane/session-restored-banner-pane-state.test.tsx": 64, + "src/renderer/src/components/terminal-pane/shell-ready-marker-scan.test.ts": 6, + "src/renderer/src/components/terminal-pane/shutdown-buffer-captures.test.ts": 9, + "src/renderer/src/components/terminal-pane/sleeping-record-park-exemption.test.ts": 7, + "src/renderer/src/components/terminal-pane/split-right-white-screen.test.ts": 8, + "src/renderer/src/components/terminal-pane/ssh-pane-connect-gate.test.ts": 9, + "src/renderer/src/components/terminal-pane/ssh-reattach-model-restore.test.ts": 16, + "src/renderer/src/components/terminal-pane/ssh-reconnect-model-paint-gate.test.ts": 7, + "src/renderer/src/components/terminal-pane/stale-document-visibility.test.ts": 34, + "src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts": 15, + "src/renderer/src/components/terminal-pane/terminal-agent-session-continuation.test.ts": 13, + "src/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.ts": 152, + "src/renderer/src/components/terminal-pane/terminal-alternate-screen-parse.test.ts": 30, + "src/renderer/src/components/terminal-pane/terminal-appearance.test.ts": 28, + "src/renderer/src/components/terminal-pane/terminal-bracketed-paste.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-capability-replies.test.ts": 66, + "src/renderer/src/components/terminal-pane/terminal-captured-input-dispatch.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-cjk-cursor-cell-placement.test.ts": 68, + "src/renderer/src/components/terminal-pane/terminal-clipboard-event-paste.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-clipboard-paste.test.ts": 12, + "src/renderer/src/components/terminal-pane/terminal-cold-park-exempt-flip.react185.test.tsx": 37, + "src/renderer/src/components/terminal-pane/terminal-cold-park-pre-gate-loop.react185.test.tsx": 64, + "src/renderer/src/components/terminal-pane/terminal-cold-park-recheck-deadlines.test.ts": 12, + "src/renderer/src/components/terminal-pane/terminal-cold-park-subscription-narrowing.react185.test.tsx": 28, + "src/renderer/src/components/terminal-pane/terminal-cold-park-tab-model-identity.react185.test.tsx": 63, + "src/renderer/src/components/terminal-pane/terminal-cold-park-timer-rearm.test.tsx": 69, + "src/renderer/src/components/terminal-pane/terminal-cold-park-verdict-loop.test.tsx": 39, + "src/renderer/src/components/terminal-pane/terminal-cold-park-withheld-tabs.test.ts": 9, + "src/renderer/src/components/terminal-pane/terminal-command-lifecycle.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-context-menu-dismiss.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-copy-rejection-handling.test.ts": 11, + "src/renderer/src/components/terminal-pane/terminal-ctrl-arrow-conpty.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-ctrl-enter.test.ts": 15, + "src/renderer/src/components/terminal-pane/terminal-cursor-appearance-precedence.test.ts": 88, + "src/renderer/src/components/terminal-pane/terminal-cursor-inactive-style.test.ts": 12, + "src/renderer/src/components/terminal-pane/terminal-dead-session-reconcile.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-delivery-watchdog.test.ts": 50, + "src/renderer/src/components/terminal-pane/terminal-drop-handler.test.ts": 34, + "src/renderer/src/components/terminal-pane/terminal-drop-image-path.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-drop-internal-handler.test.ts": 28, + "src/renderer/src/components/terminal-pane/terminal-drop-path-writer.test.ts": 21, + "src/renderer/src/components/terminal-pane/terminal-drop-runtime-owner.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-drop-shell.test.ts": 6, + "src/renderer/src/components/terminal-pane/terminal-drop-upload-report.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-drop-write-failure.test.ts": 6, + "src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts": 15, + "src/renderer/src/components/terminal-pane/terminal-error-remote-closed-localization.test.ts": 6, + "src/renderer/src/components/terminal-pane/terminal-eviction-exempt-tabs.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-file-link-actions.test.ts": 17, + "src/renderer/src/components/terminal-pane/terminal-fit-restore.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-freeze-report.test.ts": 27, + "src/renderer/src/components/terminal-pane/terminal-handle-copy.test.ts": 11, + "src/renderer/src/components/terminal-pane/terminal-handle-links.test.ts": 36, + "src/renderer/src/components/terminal-pane/terminal-hidden-restore-scrollback.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-hidden-view-parking.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-hidden-worktree-retention.test.ts": 11, + "src/renderer/src/components/terminal-pane/terminal-http-link-activation.test.ts": 4, + "src/renderer/src/components/terminal-pane/terminal-http-link-source-owner.test.ts": 11, + "src/renderer/src/components/terminal-pane/terminal-http-url-extraction.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-ime-candidate-key-release-guard.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-ime-composer-placeholder-mask.test.ts": 687, + "src/renderer/src/components/terminal-pane/terminal-ime-composition-route.test.ts": 23, + "src/renderer/src/components/terminal-pane/terminal-ime-composition-tracker.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-ime-composition-transaction-ownership.test.ts": 17, + "src/renderer/src/components/terminal-pane/terminal-ime-deferred-chord.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-ime-deferred-newline.test.ts": 28, + "src/renderer/src/components/terminal-pane/terminal-ime-forwarder-space-claim.test.ts": 89, + "src/renderer/src/components/terminal-pane/terminal-ime-hangul-syllable-flush.test.ts": 261, + "src/renderer/src/components/terminal-pane/terminal-ime-hangul-terminating-digit.test.ts": 9, + "src/renderer/src/components/terminal-pane/terminal-ime-input-context-refresh.test.ts": 22, + "src/renderer/src/components/terminal-pane/terminal-ime-linux-candidate-state.test.ts": 14, + "src/renderer/src/components/terminal-pane/terminal-ime-macos-keybinding-dict-trace.test.ts": 134, + "src/renderer/src/components/terminal-pane/terminal-ime-native-text-forwarder.test.ts": 35, + "src/renderer/src/components/terminal-pane/terminal-ime-substituted-text-commit.test.ts": 160, + "src/renderer/src/components/terminal-pane/terminal-ime-won-composition-order.test.ts": 160, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-adversarial.test.ts": 1021, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-cancelled-preedit-visibility.test.ts": 152, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-composition-cancel.test.ts": 133, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-composition-deduplication.test.ts": 446, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-consumed-key-commit.test.ts": 157, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-korean-enter-commit-order.test.ts": 222, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-linux-native-trace-replay.test.ts": 2429, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-midline-preedit-tail.test.ts": 270, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-resumed-preedit-visibility.test.ts": 111, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-trailing-preedit-occlusion.test.ts": 206, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-transaction-events.test.ts": 277, + "src/renderer/src/components/terminal-pane/terminal-ime-xterm-windows-resumed-preedit-trace.test.ts": 273, + "src/renderer/src/components/terminal-pane/terminal-input-host-platform.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-input-quarantine.test.ts": 13, + "src/renderer/src/components/terminal-pane/terminal-ios-hangul-preedit-coexistence.test.ts": 295, + "src/renderer/src/components/terminal-pane/terminal-ios-hangul-preedit-device-trace.test.ts": 302, + "src/renderer/src/components/terminal-pane/terminal-ios-hangul-preedit.test.ts": 353, + "src/renderer/src/components/terminal-pane/terminal-jis-yen-input.test.ts": 4, + "src/renderer/src/components/terminal-pane/terminal-keyboard-event-handlers-focus.test.ts": 17, + "src/renderer/src/components/terminal-pane/terminal-keyboard-pane-resolution.test.ts": 12, + "src/renderer/src/components/terminal-pane/terminal-keyboard-protocol-pane-agent.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-kitty-csi-u-encoding.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-layout-duplicate-pty-replay.test.ts": 12, + "src/renderer/src/components/terminal-pane/terminal-layout-leaf-detach.test.ts": 12, + "src/renderer/src/components/terminal-pane/terminal-layout-leaf-ids.test.ts": 11, + "src/renderer/src/components/terminal-pane/terminal-layout-overlay-focus.test.tsx": 27, + "src/renderer/src/components/terminal-pane/terminal-layout-pty-ownership-depth.test.ts": 67, + "src/renderer/src/components/terminal-pane/terminal-layout-pty-ownership.test.ts": 26, + "src/renderer/src/components/terminal-pane/terminal-link-action-routing.test.ts": 29, + "src/renderer/src/components/terminal-pane/terminal-link-activation.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-link-click-fallback.test.ts": 66, + "src/renderer/src/components/terminal-pane/terminal-link-file-open-routing.test.ts": 52, + "src/renderer/src/components/terminal-pane/terminal-link-open-hints.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-link-osc-file-targets.test.ts": 64, + "src/renderer/src/components/terminal-pane/terminal-link-osc-url-routing.test.ts": 27, + "src/renderer/src/components/terminal-pane/terminal-link-pointer-gesture.test.ts": 11, + "src/renderer/src/components/terminal-pane/terminal-link-provider-link-detection.test.ts": 91, + "src/renderer/src/components/terminal-pane/terminal-link-pty-mouse-suppression.test.ts": 35, + "src/renderer/src/components/terminal-pane/terminal-link-remote-runtime-ssh-open.test.ts": 32, + "src/renderer/src/components/terminal-pane/terminal-link-worktree-root-activation.test.ts": 40, + "src/renderer/src/components/terminal-pane/terminal-link-wsl-path-mapping.test.ts": 42, + "src/renderer/src/components/terminal-pane/terminal-linkifier-click-priming.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-live-layout-reconciliation.test.ts": 19, + "src/renderer/src/components/terminal-pane/terminal-non-latin-control-chord.test.ts": 6, + "src/renderer/src/components/terminal-pane/terminal-option-kitty-release.test.ts": 9, + "src/renderer/src/components/terminal-pane/terminal-output-visibility.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-pane-attention-subscriptions.test.ts": 6, + "src/renderer/src/components/terminal-pane/terminal-pane-close-identity.test.ts": 4, + "src/renderer/src/components/terminal-pane/terminal-pane-hook-order-parity.test.ts": 111, + "src/renderer/src/components/terminal-pane/terminal-pane-host-state-memo.test.ts": 21, + "src/renderer/src/components/terminal-pane/terminal-pane-host-state.test.ts": 13, + "src/renderer/src/components/terminal-pane/terminal-pane-listener-order-parity.test.ts": 196, + "src/renderer/src/components/terminal-pane/terminal-pane-recovery-unsettled-fallback.test.ts": 14, + "src/renderer/src/components/terminal-pane/terminal-pane-recovery.test.ts": 44, + "src/renderer/src/components/terminal-pane/terminal-pane-split-completion.test.ts": 9, + "src/renderer/src/components/terminal-pane/terminal-pane-split-request-routing.test.ts": 20, + "src/renderer/src/components/terminal-pane/terminal-pane-split-with-inherited-cwd.test.ts": 17, + "src/renderer/src/components/terminal-pane/terminal-pane-split-writer-paths.test.ts": 15, + "src/renderer/src/components/terminal-pane/terminal-pane-store-subscription-budget.test.tsx": 128, + "src/renderer/src/components/terminal-pane/terminal-pane-surface-ownership.test.ts": 23, + "src/renderer/src/components/terminal-pane/terminal-pane-tab-detach.test.ts": 25, + "src/renderer/src/components/terminal-pane/terminal-park-verdict-flip-telemetry.test.ts": 19, + "src/renderer/src/components/terminal-pane/terminal-park-verdict-worktree-driver-loop.test.tsx": 45, + "src/renderer/src/components/terminal-pane/terminal-parked-pane-candidates.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-parked-tab-eviction-exemption.test.ts": 14, + "src/renderer/src/components/terminal-pane/terminal-parked-tab-watchers-batch-sync.test.ts": 92, + "src/renderer/src/components/terminal-pane/terminal-parked-tab-watchers.test.ts": 69, + "src/renderer/src/components/terminal-pane/terminal-parked-watcher-partial-reconciliation.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-parked-watcher-registry.test.ts": 6, + "src/renderer/src/components/terminal-pane/terminal-parked-watcher-sleep-preserved-exit.test.ts": 13, + "src/renderer/src/components/terminal-pane/terminal-parked-watcher-sole-newborn-exit.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-parking-e2e-overrides.test.ts": 2548, + "src/renderer/src/components/terminal-pane/terminal-parser-handler-guard.test.ts": 18, + "src/renderer/src/components/terminal-pane/terminal-paste-coordinator.test.ts": 39, + "src/renderer/src/components/terminal-pane/terminal-paste-executor-default-yield.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-paste-multiline-policy.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-paste-operation-order.test.ts": 30, + "src/renderer/src/components/terminal-pane/terminal-paste-payload-metadata.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-paste-runtime.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-paste-target-state.test.ts": 9, + "src/renderer/src/components/terminal-pane/terminal-process-exit-restart.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-programmatic-text-paste.test.ts": 84, + "src/renderer/src/components/terminal-pane/terminal-pty-ack-gate.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-pty-paste-writer.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-quick-command-dispatch.test.ts": 14, + "src/renderer/src/components/terminal-pane/terminal-remote-runtime-recovery-ui-state.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-render-desync-frame.test.ts": 4, + "src/renderer/src/components/terminal-pane/terminal-render-desync-sentinel.test.ts": 395, + "src/renderer/src/components/terminal-pane/terminal-render-desync-weight-probe.test.ts": 9, + "src/renderer/src/components/terminal-pane/terminal-renderer-policy.test.ts": 11, + "src/renderer/src/components/terminal-pane/terminal-replay-application-continuity.test.ts": 266, + "src/renderer/src/components/terminal-pane/terminal-replay-cursor-state.test.ts": 70, + "src/renderer/src/components/terminal-pane/terminal-restore-sgr-latch.test.ts": 62, + "src/renderer/src/components/terminal-pane/terminal-restored-viewport.test.ts": 25, + "src/renderer/src/components/terminal-pane/terminal-retention-exempt-growth.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-runtime-host-link-routing.test.ts": 14, + "src/renderer/src/components/terminal-pane/terminal-selection-copy.test.ts": 10, + "src/renderer/src/components/terminal-pane/terminal-shortcut-ctrl-arrow.test.ts": 13, + "src/renderer/src/components/terminal-pane/terminal-shortcut-option-compose.test.ts": 29, + "src/renderer/src/components/terminal-pane/terminal-shortcut-policy.test.ts": 27, + "src/renderer/src/components/terminal-pane/terminal-shortcut-select-all.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-shutdown-layout-capture.test.ts": 139, + "src/renderer/src/components/terminal-pane/terminal-side-effect-facts-handler.test.ts": 17, + "src/renderer/src/components/terminal-pane/terminal-snapshot-replay-paint.test.ts": 287, + "src/renderer/src/components/terminal-pane/terminal-startup-command-classifier.test.ts": 7, + "src/renderer/src/components/terminal-pane/terminal-startup-grid-settle.test.ts": 14, + "src/renderer/src/components/terminal-pane/terminal-tab-agent-type-index.test.ts": 5, + "src/renderer/src/components/terminal-pane/terminal-tab-lookup.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-title-evidence.test.ts": 12, + "src/renderer/src/components/terminal-pane/terminal-title-tracker-parity.test.ts": 31, + "src/renderer/src/components/terminal-pane/terminal-unified-tab-lookup.test.ts": 16, + "src/renderer/src/components/terminal-pane/terminal-url-link-click.test.ts": 29, + "src/renderer/src/components/terminal-pane/terminal-url-link-hit-testing.test.ts": 14, + "src/renderer/src/components/terminal-pane/terminal-user-input-signal.test.ts": 15, + "src/renderer/src/components/terminal-pane/terminal-view-attributes-publisher.test.ts": 23, + "src/renderer/src/components/terminal-pane/terminal-visibility-resume.test.ts": 31, + "src/renderer/src/components/terminal-pane/terminal-webgl-atlas-recovery.test.ts": 8, + "src/renderer/src/components/terminal-pane/terminal-webgl-diagnostics-breadcrumbs.test.ts": 9, + "src/renderer/src/components/terminal-pane/terminal-windows-shift-enter.test.ts": 19, + "src/renderer/src/components/terminal-pane/terminal-worktree-path-link.test.ts": 6, + "src/renderer/src/components/terminal-pane/title-agent-identity.test.ts": 6, + "src/renderer/src/components/terminal-pane/use-manual-terminal-worktree-parking.test.ts": 3, + "src/renderer/src/components/terminal-pane/use-mobile-overlay-ticks.perf.test.tsx": 31, + "src/renderer/src/components/terminal-pane/use-notification-dispatch.test.ts": 44, + "src/renderer/src/components/terminal-pane/use-parked-terminal-watcher-synchronization.react185.test.tsx": 59, + "src/renderer/src/components/terminal-pane/use-system-prefers-dark.test.ts": 7, + "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects-active-pty-reporting.test.ts": 15, + "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects-file-drop.test.ts": 22, + "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects-paste-events.test.ts": 43, + "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects-sync-fit-registration.test.ts": 14, + "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects-visibility-resume.test.ts": 22, + "src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects-window-focus-recovery.test.ts": 28, + "src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.test.ts": 19, + "src/renderer/src/components/terminal-pane/use-terminal-park-mount-intent.react185.test.tsx": 26, + "src/renderer/src/components/terminal-pane/use-terminal-scroll-visibility-memory.test.ts": 9, + "src/renderer/src/components/terminal-pane/use-terminal-tab-cold-parking.test.ts": 99, + "src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts": 55, + "src/renderer/src/components/terminal-pane/use-visible-terminal-tab-claim.test.ts": 5, + "src/renderer/src/components/terminal-pane/useSessionRestoredBannerDismiss.test.tsx": 29, + "src/renderer/src/components/terminal-pane/useTerminalFontZoom.test.ts": 10, + "src/renderer/src/components/terminal-pane/wrapped-terminal-link-ranges.test.ts": 25, + "src/renderer/src/components/terminal-pane/xterm-bypass-policy-interrupt.test.ts": 13, + "src/renderer/src/components/terminal-pane/xterm-bypass-policy-ios.test.ts": 14, + "src/renderer/src/components/terminal-pane/xterm-bypass-policy-non-mac.test.ts": 14, + "src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts": 9, + "src/renderer/src/components/terminal-pane/xterm-write-buffer-stall.repro.test.ts": 24, + "src/renderer/src/components/terminal-parked-watcher-sync-entries.test.tsx": 37, + "src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.test.tsx": 660, + "src/renderer/src/components/terminal-quick-commands/terminal-quick-command-agent-options.test.ts": 10, + "src/renderer/src/components/terminal-quick-commands/terminal-quick-command-dialog-draft.test.ts": 11, + "src/renderer/src/components/terminal-scrollback-decoration-eviction.test.ts": 414, + "src/renderer/src/components/terminal-search-decoration-leak.test.ts": 1942, + "src/renderer/src/components/terminal-search-long-wrapped-line.test.ts": 4994, + "src/renderer/src/components/terminal-search-safe-find.test.ts": 7, + "src/renderer/src/components/terminal-workspace-keydown.test.ts": 246, + "src/renderer/src/components/terminal-workspace-surface-ids.test.tsx": 87, + "src/renderer/src/components/terminal/activation-deferred-tab-admission.test.ts": 13, + "src/renderer/src/components/terminal/active-terminal-repair-loop.react185.test.tsx": 38, + "src/renderer/src/components/terminal/active-terminal-repair.test.ts": 6, + "src/renderer/src/components/terminal/background-terminal-worktree-mount.test.ts": 24, + "src/renderer/src/components/terminal/background-terminal-worktree-visibility.test.ts": 14, + "src/renderer/src/components/terminal/cold-activation-deferral-stranding.test.ts": 37, + "src/renderer/src/components/terminal/initial-terminal-structured-launch.test.tsx": 26, + "src/renderer/src/components/terminal/initial-terminal-wiring.test.ts": 4, + "src/renderer/src/components/terminal/initial-terminal.test.ts": 4, + "src/renderer/src/components/terminal/pty-running-work-probe-child-evidence.test.ts": 9, + "src/renderer/src/components/terminal/running-terminal-close-guard.test.ts": 18, + "src/renderer/src/components/terminal/split-group-mount.test.ts": 8, + "src/renderer/src/components/terminal/tab-type-cycle.test.ts": 9, + "src/renderer/src/components/terminal/terminal-close-confirm-keyboard-vs-mouse.test.ts": 12, + "src/renderer/src/components/terminal/terminal-close-copy-kind.test.ts": 8, + "src/renderer/src/components/terminal/terminal-close-incarnation.test.ts": 5, + "src/renderer/src/components/terminal/terminal-provider-snapshot-bound-pty-ids.test.ts": 74, + "src/renderer/src/components/terminal/terminal-provider-snapshot-capability-resettlement.test.ts": 15, + "src/renderer/src/components/terminal/terminal-provider-snapshot-capability.test.ts": 15, + "src/renderer/src/components/terminal/terminal-tab-actions-kill-all.test.ts": 15, + "src/renderer/src/components/terminal/terminal-tab-actions-structured-session.test.ts": 17, + "src/renderer/src/components/terminal/terminal-tab-actions-unified-close.test.ts": 32, + "src/renderer/src/components/terminal/terminal-tab-actions.test.ts": 32, + "src/renderer/src/components/terminal/terminal-tab-bulk-actions.test.ts": 12, + "src/renderer/src/components/terminal/terminal-tab-close-running-confirm.test.ts": 21, + "src/renderer/src/components/terminal/unsaved-close-queue.test.ts": 5, + "src/renderer/src/components/terminal/use-terminal-provider-snapshot-capability.test.tsx": 50, + "src/renderer/src/components/terminal/use-worktree-files.test.tsx": 52, + "src/renderer/src/components/terminal/window-close-running-work.test.ts": 23, + "src/renderer/src/components/ui/color-picker.test.tsx": 24, + "src/renderer/src/components/ui/popover-wheel-scroll.test.tsx": 243, + "src/renderer/src/components/ui/repo-multi-combobox.test.ts": 6, + "src/renderer/src/components/ui/scroll-area.test.tsx": 39, + "src/renderer/src/components/ui/slider.test.tsx": 111, + "src/renderer/src/components/ui/switch.test.tsx": 29, + "src/renderer/src/components/unexpected-signout/unexpected-signout-card.test.tsx": 206, + "src/renderer/src/components/unexpected-signout/unexpected-signout-visibility.test.ts": 9, + "src/renderer/src/components/use-github-task-search-commit.test.ts": 35, + "src/renderer/src/components/use-task-creation-draft-retention.test.ts": 25, + "src/renderer/src/components/use-terminal-create-actions.test.tsx": 22, + "src/renderer/src/components/use-terminal-editor-close-foundation.window-close.test.tsx": 28, + "src/renderer/src/components/use-terminal-window-lifecycle.lazy-ref.test.tsx": 16, + "src/renderer/src/components/use-worktree-jump-palette-browser-ownership.test.ts": 37, + "src/renderer/src/components/window-close-request-coordinator.test.ts": 19, + "src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.mount-gating.test.tsx": 100, + "src/renderer/src/components/workspace-cleanup/WorkspaceCleanupDialog.stale-while-revalidate.test.tsx": 1307, + "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-browse-state.test.tsx": 43, + "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-facet-rows.test.tsx": 58, + "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-git-evidence.test.tsx": 60, + "src/renderer/src/components/workspace-cleanup/use-workspace-cleanup-row-order.test.ts": 26, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-active-facets.test.ts": 11, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal-late-settlement.test.ts": 15, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal-snapshot-batch.test.ts": 63, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-background-removal.test.ts": 31, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-list.test.tsx": 47, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.test.ts": 6, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.test.tsx": 214, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-remove.test.tsx": 118, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-deletion-phases.test.ts": 9, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-dialog-notices.test.tsx": 59, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-controls.test.tsx": 357, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-filter.test.ts": 39, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.test.ts": 12, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-filter-bar.test.tsx": 237, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-flat-list.test.tsx": 280, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-git-evidence.test.ts": 16, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-presentation.test.ts": 27, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-candidates.test.ts": 7, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-settlement.test.ts": 9, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-removal-timeout-recovery.test.ts": 26, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-scanned-host-confirmation-removal.test.tsx": 470, + "src/renderer/src/components/workspace-cleanup/workspace-cleanup-sort-header.test.tsx": 82, + "src/renderer/src/components/workspace-emoji/WorkspaceEmojiSuggestionPopover.test.tsx": 83, + "src/renderer/src/components/workspace-emoji/useWorkspaceEmojiShortcodeInput.test.tsx": 122, + "src/renderer/src/components/workspace-surface-projection.test.ts": 39, + "src/renderer/src/components/worktree-creation/WorktreeCreationPanel.test.tsx": 74, + "src/renderer/src/components/worktree-jump-palette-interleaved-sections.test.tsx": 1645, + "src/renderer/src/components/worktree-jump-palette-primitives.test.tsx": 131, + "src/renderer/src/components/worktree-jump-palette-quick-action-availability.test.tsx": 20, + "src/renderer/src/components/worktree-jump-palette-sleeping-filter.test.ts": 9, + "src/renderer/src/components/worktree-jump-palette-source-context-boundary.test.ts": 4, + "src/renderer/src/components/worktree-jump-palette-status-inputs.test.ts": 6, + "src/renderer/src/hooks/agent-hook-completion-background-turn-notifications.test.ts": 134, + "src/renderer/src/hooks/agent-hook-completion-fresh-working.test.ts": 109, + "src/renderer/src/hooks/agent-hook-completion-notifications-import.test.ts": 92, + "src/renderer/src/hooks/agent-hook-completion-notifications.test.ts": 264, + "src/renderer/src/hooks/agent-hook-completion-store-sync.test.ts": 18, + "src/renderer/src/hooks/automation-agent-status-entry-change.test.ts": 7, + "src/renderer/src/hooks/automation-dispatch-unverifiable-loss.test.ts": 137, + "src/renderer/src/hooks/automations-changed-event-attribution.test.ts": 1424, + "src/renderer/src/hooks/composer-branch-selection.test.ts": 11, + "src/renderer/src/hooks/composer-drop-owner.test.ts": 4, + "src/renderer/src/hooks/composer-drop-upload-result.test.ts": 8, + "src/renderer/src/hooks/composer-native-file-drop.test.ts": 9, + "src/renderer/src/hooks/composer-state/composer-drop-listener.test.ts": 26, + "src/renderer/src/hooks/composer-state/draft-target-sync.test.ts": 24, + "src/renderer/src/hooks/composer-state/full-creation-execution.test.ts": 23, + "src/renderer/src/hooks/composer-state/full-creation-structured-launch.test.ts": 7, + "src/renderer/src/hooks/composer-state/full-submit-orchestration.test.ts": 22, + "src/renderer/src/hooks/composer-state/multiple-create-reset.test.ts": 29, + "src/renderer/src/hooks/composer-state/provider-runtime-sync.test.ts": 18, + "src/renderer/src/hooks/composer-state/quick-creation-request.test.ts": 10, + "src/renderer/src/hooks/direct-ssh-host-hydration.test.ts": 24, + "src/renderer/src/hooks/direct-ssh-reconnect-coordinator.test.ts": 30, + "src/renderer/src/hooks/direct-ssh-reconnect-rollout.test.ts": 11, + "src/renderer/src/hooks/direct-ssh-reconnect-tokens.test.ts": 6, + "src/renderer/src/hooks/direct-ssh-runtime-wake-isolation.test.tsx": 43, + "src/renderer/src/hooks/direct-ssh-state-routing.test.ts": 16, + "src/renderer/src/hooks/direct-ssh-worktree-refresh-scheduler.test.ts": 27, + "src/renderer/src/hooks/fork-push-warning.test.ts": 5, + "src/renderer/src/hooks/installed-agent-skill-discovery-cache.test.ts": 18, + "src/renderer/src/hooks/installed-agent-skill-discovery.test.ts": 9, + "src/renderer/src/hooks/ipc-events/agent-status-pane-routing-index.test.ts": 79, + "src/renderer/src/hooks/ipc-events/agent-status-pending-retry-gate.test.ts": 272, + "src/renderer/src/hooks/ipc-events/agent-status-terminal-title-tab-write.test.ts": 186, + "src/renderer/src/hooks/ipc-events/browser-automation-bootstrap-lease.test.ts": 10, + "src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.profile-switch.test.ts": 7, + "src/renderer/src/hooks/ipc-events/browser-state-open-link-profile.test.ts": 8, + "src/renderer/src/hooks/ipc-events/direct-ssh-hydration-fanout.test.ts": 12, + "src/renderer/src/hooks/ipc-events/direct-ssh-hydration-target-metadata.test.ts": 21, + "src/renderer/src/hooks/ipc-events/normalize-agent-status-event.test.ts": 6, + "src/renderer/src/hooks/ipc-events/orca-profile-auth-ipc-bridge.test.ts": 111, + "src/renderer/src/hooks/ipc-events/os-markdown-file-open-bridge.test.ts": 16, + "src/renderer/src/hooks/ipc-events/runtime-reconnect-host-status.test.ts": 32, + "src/renderer/src/hooks/ipc-events/terminal-ui-routing-ipc-bridge-split.test.ts": 15, + "src/renderer/src/hooks/ipc-events/updater-status-ipc-bridge.test.ts": 6, + "src/renderer/src/hooks/ipc-tab-switch-group-order-hydration.test.ts": 11, + "src/renderer/src/hooks/ipc-tab-switch-structured-tab-cycle.test.ts": 11, + "src/renderer/src/hooks/ipc-tab-switch.test.ts": 17, + "src/renderer/src/hooks/legacy-worker-terminal-recovery-event.test.ts": 15, + "src/renderer/src/hooks/macos-tcc-prompt-notice-subscription.test.ts": 23, + "src/renderer/src/hooks/metadata-request-cache.test.ts": 142, + "src/renderer/src/hooks/mobile-terminal-reveal-tab-adoption.test.ts": 2166, + "src/renderer/src/hooks/modal-return-focus-action.test.ts": 7, + "src/renderer/src/hooks/programmatic-scroll-marks.test.ts": 9, + "src/renderer/src/hooks/remote-workspace-deferred-placement-retry.test.ts": 14, + "src/renderer/src/hooks/remote-workspace-session-merge-close-tombstones.test.ts": 13, + "src/renderer/src/hooks/remote-workspace-session-merge-closed-terminal-tombstone.test.ts": 39, + "src/renderer/src/hooks/remote-workspace-session-merge-local-survival.test.ts": 12, + "src/renderer/src/hooks/remote-workspace-snapshot-apply-deferred-session-write.test.ts": 76, + "src/renderer/src/hooks/remote-workspace-snapshot-arrival-coordinator.test.ts": 8, + "src/renderer/src/hooks/remote-workspace-snapshot-duplicate-tab-repair.test.ts": 68, + "src/renderer/src/hooks/remote-workspace-snapshot-fresh-client-tab-seeding.test.ts": 19, + "src/renderer/src/hooks/remote-workspace-snapshot-local-tab-survival.test.ts": 108, + "src/renderer/src/hooks/remote-workspace-snapshot-unplaced-tab-adoption.test.ts": 80, + "src/renderer/src/hooks/remote-workspace-target-sync.test.ts": 45, + "src/renderer/src/hooks/runtime-client-events-sync.test.ts": 66, + "src/renderer/src/hooks/runtime-project-refresh-scheduler.test.ts": 51, + "src/renderer/src/hooks/shortcut-label-cache.test.tsx": 65, + "src/renderer/src/hooks/ssh-reconnect-pane-retry.test.ts": 7, + "src/renderer/src/hooks/structured-session-completion-focus.test.ts": 1364, + "src/renderer/src/hooks/unpaired-device-auth-notification.test.ts": 6, + "src/renderer/src/hooks/use-audio-capture.capture-loss.test.ts": 31, + "src/renderer/src/hooks/use-clipboard-text-copy-feedback.test.ts": 28, + "src/renderer/src/hooks/use-now.test.ts": 35, + "src/renderer/src/hooks/use-palette-search-evaluation-context.test.ts": 29, + "src/renderer/src/hooks/use-task-page-github-work-item-mutation-host.test.ts": 7, + "src/renderer/src/hooks/use-terminal-quick-command-hosts.test.ts": 59, + "src/renderer/src/hooks/use-window-stream-visibility.test.ts": 37, + "src/renderer/src/hooks/useActiveProjectSkillRuntime.test.tsx": 57, + "src/renderer/src/hooks/useAgentDetectionTarget.test.ts": 11, + "src/renderer/src/hooks/useAppMenuPaste.test.tsx": 29, + "src/renderer/src/hooks/useAppMenuSelectionActions.test.tsx": 13, + "src/renderer/src/hooks/useAutoAckViewedAgent.away.test.ts": 95, + "src/renderer/src/hooks/useAutoAckViewedAgent.clock-skew.test.ts": 55, + "src/renderer/src/hooks/useAutoAckViewedAgent.floating-panel.test.ts": 38, + "src/renderer/src/hooks/useAutoAckViewedAgent.test.ts": 63, + "src/renderer/src/hooks/useAutomationDispatchEvents.test.ts": 692, + "src/renderer/src/hooks/useComposerState-decisions.test.ts": 9, + "src/renderer/src/hooks/useComposerState-host-context-boundaries.test.ts": 21, + "src/renderer/src/hooks/useComposerState-host-retarget.test.ts": 7, + "src/renderer/src/hooks/useComposerState.integration.test.ts": 39, + "src/renderer/src/hooks/useDetectedAgents.test.tsx": 43, + "src/renderer/src/hooks/useEditorExternalWatch-complexity.test.ts": 6402, + "src/renderer/src/hooks/useEditorExternalWatch-self-move.test.ts": 27, + "src/renderer/src/hooks/useEditorExternalWatch-subscriptions.test.tsx": 24, + "src/renderer/src/hooks/useEditorExternalWatch-targets.test.ts": 21, + "src/renderer/src/hooks/useEditorExternalWatch-wsl-repro.test.ts": 21, + "src/renderer/src/hooks/useEditorExternalWatch.test.ts": 54, + "src/renderer/src/hooks/useEphemeralVmRecipeOptions.test.tsx": 33, + "src/renderer/src/hooks/useGitHubSlugMetadata.test.tsx": 47, + "src/renderer/src/hooks/useGlobalFileDrop.test.ts": 15, + "src/renderer/src/hooks/useInstalledAgentSkills.react.test.tsx": 90, + "src/renderer/src/hooks/useInstalledAgentSkills.test.ts": 17, + "src/renderer/src/hooks/useIpcEvents-agent-dashboard-shortcut.test.ts": 9, + "src/renderer/src/hooks/useIpcEvents-agent-status-batch-projection.test.ts": 1128, + "src/renderer/src/hooks/useIpcEvents-agent-status-connection-attribution.test.ts": 1955, + "src/renderer/src/hooks/useIpcEvents-agent-status-hook-titles.test.ts": 1349, + "src/renderer/src/hooks/useIpcEvents-agent-status-pane-teardown.test.ts": 2111, + "src/renderer/src/hooks/useIpcEvents-agent-status-queue-ordering.test.ts": 1997, + "src/renderer/src/hooks/useIpcEvents-agent-status-snapshot-hydration.test.ts": 2101, + "src/renderer/src/hooks/useIpcEvents-agent-status-snapshot-replay.test.ts": 1690, + "src/renderer/src/hooks/useIpcEvents-agent-status-ssh-authority.test.ts": 3444, + "src/renderer/src/hooks/useIpcEvents-agent-status-turn-completion.test.ts": 1433, + "src/renderer/src/hooks/useIpcEvents-browser-certificate-failure.test.ts": 1719, + "src/renderer/src/hooks/useIpcEvents-browser-navigation.test.ts": 1370, + "src/renderer/src/hooks/useIpcEvents-browser-tab-create.test.ts": 1568, + "src/renderer/src/hooks/useIpcEvents-cli-worktree-activation.test.ts": 1650, + "src/renderer/src/hooks/useIpcEvents-client-hosted-browser-rows.test.ts": 1604, + "src/renderer/src/hooks/useIpcEvents-close-routing-active-browser-tab.test.ts": 1787, + "src/renderer/src/hooks/useIpcEvents-close-routing-browser-pages.test.ts": 2510, + "src/renderer/src/hooks/useIpcEvents-close-routing-floating-guest.test.ts": 1438, + "src/renderer/src/hooks/useIpcEvents-close-routing-session-tabs.test.ts": 2721, + "src/renderer/src/hooks/useIpcEvents-cmd-j-digit-chord.test.ts": 1311, + "src/renderer/src/hooks/useIpcEvents-lifecycle.test.ts": 1102, + "src/renderer/src/hooks/useIpcEvents-new-workspace-shortcut.test.ts": 12, + "src/renderer/src/hooks/useIpcEvents-rate-limit-hydration.test.ts": 1455, + "src/renderer/src/hooks/useIpcEvents-repos-changed-catalogs.test.ts": 1275, + "src/renderer/src/hooks/useIpcEvents-runtime-environment-selectors.test.ts": 17, + "src/renderer/src/hooks/useIpcEvents-session-tab-close-request.test.ts": 1943, + "src/renderer/src/hooks/useIpcEvents-silent-terminal-adoption.test.ts": 1945, + "src/renderer/src/hooks/useIpcEvents-ssh-disconnect-cleanup.test.ts": 1453, + "src/renderer/src/hooks/useIpcEvents-terminal-create-surfacing.test.ts": 2086, + "src/renderer/src/hooks/useIpcEvents-updater-status.test.ts": 1582, + "src/renderer/src/hooks/useIpcEvents-zoom-routing.test.ts": 1559, + "src/renderer/src/hooks/useIssueMetadata.test.tsx": 27, + "src/renderer/src/hooks/useLinearProviderConnected.test.tsx": 19, + "src/renderer/src/hooks/useMacTccAttributionSeveredNotice.test.tsx": 320, + "src/renderer/src/hooks/useMacosTccPromptNotice.test.tsx": 207, + "src/renderer/src/hooks/useMetadataListRequest.test.tsx": 20, + "src/renderer/src/hooks/useModalReturnFocus.test.tsx": 64, + "src/renderer/src/hooks/usePrimarySelectionPaste.terminal-native-paste.test.tsx": 32, + "src/renderer/src/hooks/usePrimarySelectionPaste.test.tsx": 55, + "src/renderer/src/hooks/useResetCountdownClock.test.ts": 23, + "src/renderer/src/hooks/useRetiredWorktreeNames.test.ts": 344, + "src/renderer/src/hooks/useSettingsNavigationMetadata.capability-owner.test.tsx": 85, + "src/renderer/src/hooks/useSettingsNavigationMetadata.language-switch.test.tsx": 235, + "src/renderer/src/hooks/useSettingsNavigationMetadata.test.ts": 184, + "src/renderer/src/hooks/useSidebarResize.test.ts": 9, + "src/renderer/src/hooks/useSkillFreshness.test.tsx": 65, + "src/renderer/src/hooks/useUnreadDockBadge.test.ts": 128, + "src/renderer/src/hooks/useVirtualizedScrollAnchor.listener-deps.test.ts": 30, + "src/renderer/src/hooks/useVirtualizedScrollAnchor.marks-restore-signal.test.ts": 31, + "src/renderer/src/hooks/useVirtualizedScrollAnchor.test.ts": 8, + "src/renderer/src/hooks/useWindowsTerminalCapabilityOwnerKey.test.tsx": 46, + "src/renderer/src/hooks/viewport-size-change-listener.test.ts": 6, + "src/renderer/src/hooks/worktree-change-refresh-queue.test.ts": 14, + "src/renderer/src/hooks/worktree-head-identity-apply.test.ts": 10, + "src/renderer/src/i18n/I18nProvider.test.tsx": 36, + "src/renderer/src/i18n/integration-card-status-localization.test.ts": 7, + "src/renderer/src/i18n/intl-locale.test.ts": 121, + "src/renderer/src/i18n/ja-technical-literal-mistranslations.test.ts": 7, + "src/renderer/src/i18n/ko-ui-semantic-mistranslations.test.ts": 9, + "src/renderer/src/i18n/lazy-locale.test.ts": 109, + "src/renderer/src/i18n/locale-english-regression.test.ts": 22, + "src/renderer/src/i18n/native-chat-locales.test.ts": 10, + "src/renderer/src/i18n/no-top-level-translate.test.ts": 2294, + "src/renderer/src/i18n/plugin-chrome-allowlist.test.ts": 11, + "src/renderer/src/i18n/pseudo-localization.test.ts": 8, + "src/renderer/src/i18n/relative-time-format.test.ts": 27, + "src/renderer/src/i18n/runtime-required-catalog.test.ts": 335, + "src/renderer/src/i18n/settings-status-label-localization.test.ts": 9, + "src/renderer/src/i18n/smart-workspace-jira-locales.test.ts": 8, + "src/renderer/src/i18n/technical-literal-catalog-values.test.ts": 8, + "src/renderer/src/i18n/weekday-names.test.ts": 40, + "src/renderer/src/i18n/worktree-visibility-locales.test.ts": 8, + "src/renderer/src/i18n/zh-technical-literal-mistranslations.test.ts": 45, + "src/renderer/src/lazy-modal-mount-state.test.ts": 5, + "src/renderer/src/lazy-use-ref-ratchet.test.ts": 165, + "src/renderer/src/lib/activate-ai-vault-structured-session-reveal.test.ts": 11, + "src/renderer/src/lib/activate-ai-vault-structured-session.test.ts": 17, + "src/renderer/src/lib/activate-tab-and-focus-pane.test.ts": 11, + "src/renderer/src/lib/active-agent-note-send-explicit-target.test.ts": 385, + "src/renderer/src/lib/active-agent-note-send-focused-session.test.ts": 74, + "src/renderer/src/lib/active-agent-note-send-runtime-error-codes.test.ts": 6, + "src/renderer/src/lib/active-agent-note-send-target-detection.test.ts": 13, + "src/renderer/src/lib/active-view-persist.test.ts": 4, + "src/renderer/src/lib/activity-thread-display.test.ts": 10, + "src/renderer/src/lib/agent-background-session-launch-host.test.ts": 12, + "src/renderer/src/lib/agent-catalog-links.test.ts": 5, + "src/renderer/src/lib/agent-draft-readiness.test.ts": 8, + "src/renderer/src/lib/agent-followup-delivery.test.ts": 18659, + "src/renderer/src/lib/agent-hibernation-confirmation.test.ts": 4, + "src/renderer/src/lib/agent-hibernation-coordinator.test.ts": 163, + "src/renderer/src/lib/agent-hibernation-output-activity.test.ts": 6, + "src/renderer/src/lib/agent-hibernation-pane-age.test.ts": 9, + "src/renderer/src/lib/agent-hibernation-planner.test.ts": 30, + "src/renderer/src/lib/agent-hibernation-runtime-liveness-race.test.ts": 65, + "src/renderer/src/lib/agent-hibernation-visibility.test.ts": 5, + "src/renderer/src/lib/agent-launch-prompt-delivery.test.ts": 14, + "src/renderer/src/lib/agent-launch-route-connection-fallback.test.ts": 6, + "src/renderer/src/lib/agent-launch-route-input.test.ts": 16, + "src/renderer/src/lib/agent-launch-routing-caller-census.test.ts": 552, + "src/renderer/src/lib/agent-launch-routing.test.ts": 11, + "src/renderer/src/lib/agent-paste-draft-readiness-budget.test.ts": 13, + "src/renderer/src/lib/agent-paste-draft-submit-retry.test.ts": 48, + "src/renderer/src/lib/agent-paste-draft.test.ts": 178, + "src/renderer/src/lib/agent-picker-search.test.ts": 28, + "src/renderer/src/lib/agent-ready-wait.test.ts": 7, + "src/renderer/src/lib/agent-resume-launch-target.test.ts": 2472, + "src/renderer/src/lib/agent-row-primary-text.test.ts": 8, + "src/renderer/src/lib/agent-row-tool-preview.test.ts": 9, + "src/renderer/src/lib/agent-send-title-status.test.ts": 14, + "src/renderer/src/lib/agent-session-fork-context.test.ts": 47, + "src/renderer/src/lib/agent-session-launch-plan.test.ts": 10, + "src/renderer/src/lib/agent-skill-cli-prerequisite.test.ts": 68, + "src/renderer/src/lib/agent-skill-nav-install-status.test.ts": 5, + "src/renderer/src/lib/agent-startup-delayed-delivery-perf.test.ts": 85, + "src/renderer/src/lib/agent-status-connection-ownership.test.ts": 7, + "src/renderer/src/lib/agent-status-count.test.ts": 14, + "src/renderer/src/lib/agent-status-epoch-clock.test.ts": 12, + "src/renderer/src/lib/agent-status-evidence-clock.test.ts": 6, + "src/renderer/src/lib/agent-status-terminal-title.test.ts": 11, + "src/renderer/src/lib/agent-status-worktree-attribution.test.ts": 6, + "src/renderer/src/lib/agent-status.test.ts": 31, + "src/renderer/src/lib/agent-tab-shortcuts.test.ts": 7, + "src/renderer/src/lib/agent-trust-preflight.test.ts": 6, + "src/renderer/src/lib/ai-vault-omp-cold-resume.test.ts": 19, + "src/renderer/src/lib/ai-vault-resume-command.drop-repin.test.ts": 6, + "src/renderer/src/lib/ai-vault-resume-command.resumable-agent.test.ts": 9, + "src/renderer/src/lib/ai-vault-resume-command.test.ts": 31, + "src/renderer/src/lib/ai-vault-resume-shell.test.ts": 5, + "src/renderer/src/lib/ai-vault-resume-target.test.ts": 15, + "src/renderer/src/lib/ai-vault-session-drag.test.ts": 15, + "src/renderer/src/lib/ai-vault-session-resume-preparation.test.ts": 16, + "src/renderer/src/lib/ai-vault-tab-title-sync-inputs.test.ts": 38, + "src/renderer/src/lib/ai-vault-tab-title-sync.test.ts": 576, + "src/renderer/src/lib/app-command-dispatch.test.ts": 6, + "src/renderer/src/lib/app-font-family.test.ts": 4, + "src/renderer/src/lib/app-menu-paste.test.ts": 49, + "src/renderer/src/lib/app-menu-selection-actions.test.ts": 4, + "src/renderer/src/lib/automation-session-observer.test.ts": 58, + "src/renderer/src/lib/automation-session-reuse.test.ts": 7, + "src/renderer/src/lib/automation-terminal-ownership.test.ts": 9, + "src/renderer/src/lib/browser-cookie-import-toast.test.ts": 18, + "src/renderer/src/lib/browser-history-match.performance.test.ts": 17, + "src/renderer/src/lib/browser-history-match.test.ts": 34, + "src/renderer/src/lib/browser-page-conversion-history.test.ts": 8, + "src/renderer/src/lib/browser-page-palette-activation.test.ts": 59, + "src/renderer/src/lib/browser-palette-page-entries.test.ts": 145, + "src/renderer/src/lib/browser-palette-search.test.ts": 28, + "src/renderer/src/lib/browser-uuid.test.ts": 5, + "src/renderer/src/lib/browser-workspace-tab-activation.test.ts": 9, + "src/renderer/src/lib/client-creation-action-policy.test.ts": 12, + "src/renderer/src/lib/cmd-j-github-url-lookup.test.ts": 8, + "src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts": 30, + "src/renderer/src/lib/cmd-j-linear-issue-intent.test.ts": 7, + "src/renderer/src/lib/cmd-j-section-leadership.test.ts": 9, + "src/renderer/src/lib/codex-account-display-label.test.ts": 6, + "src/renderer/src/lib/codex-pane-restart-eligibility.test.ts": 8, + "src/renderer/src/lib/codex-pane-selection-lane.test.ts": 21, + "src/renderer/src/lib/codex-session-restart-route-recheck.test.ts": 13, + "src/renderer/src/lib/codex-session-restart-shell-flap.test.ts": 30, + "src/renderer/src/lib/codex-session-restart.test.ts": 93, + "src/renderer/src/lib/codex-stale-pane-account-identity.test.ts": 17, + "src/renderer/src/lib/codex-stale-pane-sweep.test.ts": 44, + "src/renderer/src/lib/comment-body-line-count.test.ts": 40, + "src/renderer/src/lib/comment-body-submit-state.test.ts": 31, + "src/renderer/src/lib/composer-issue-command.test.ts": 7, + "src/renderer/src/lib/composer-submit-cancellation.test.ts": 7, + "src/renderer/src/lib/connection-context.test.ts": 56, + "src/renderer/src/lib/crash-diagnostics.test.ts": 83, + "src/renderer/src/lib/create-untitled-markdown.test.ts": 13, + "src/renderer/src/lib/desktop-window-chrome.test.ts": 5, + "src/renderer/src/lib/diff-comment-compat.test.ts": 5, + "src/renderer/src/lib/diff-comments-format.test.ts": 7, + "src/renderer/src/lib/direct-ssh-reconnect-product-telemetry.test.ts": 11, + "src/renderer/src/lib/direct-ssh-target-scope.test.ts": 12, + "src/renderer/src/lib/doc-preview-grants.test.ts": 118, + "src/renderer/src/lib/document-theme.test.ts": 10, + "src/renderer/src/lib/editable-target.test.ts": 6, + "src/renderer/src/lib/editor-file-operation-owner.test.ts": 29, + "src/renderer/src/lib/editor-font-zoom.test.ts": 5, + "src/renderer/src/lib/ensure-hooks-confirmed.test.ts": 433, + "src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts": 162, + "src/renderer/src/lib/ensure-simulator-tab.test.ts": 5, + "src/renderer/src/lib/ephemeral-vm-failed-create-cleanup.test.ts": 7, + "src/renderer/src/lib/ephemeral-vm-runtime-cleanup.test.ts": 16, + "src/renderer/src/lib/ephemeral-vm-workspace-target.integration.test.ts": 8, + "src/renderer/src/lib/ephemeral-vm-workspace-target.test.ts": 13, + "src/renderer/src/lib/ephemeral-vm-worktree-creation.test.ts": 8, + "src/renderer/src/lib/execute-open-editor-path-move.test.ts": 209, + "src/renderer/src/lib/external-editor-open-capability.test.ts": 5, + "src/renderer/src/lib/feature-education-telemetry.test.ts": 12, + "src/renderer/src/lib/feedback-image-attachments.test.ts": 28, + "src/renderer/src/lib/file-preview-action-visibility.test.tsx": 22, + "src/renderer/src/lib/file-preview.test.ts": 16, + "src/renderer/src/lib/file-search-result-owner.test.ts": 6, + "src/renderer/src/lib/file-search-selection.test.ts": 8, + "src/renderer/src/lib/file-type-icons.test.ts": 9, + "src/renderer/src/lib/find-query-bounds.test.ts": 4, + "src/renderer/src/lib/finished-agent-resume-resurrection.test.ts": 38, + "src/renderer/src/lib/fix-checks-agent-launch.test.ts": 262, + "src/renderer/src/lib/flatten-retained-slice.test.ts": 132, + "src/renderer/src/lib/floating-terminal.test.ts": 7, + "src/renderer/src/lib/floating-workspace-terminal-actions.test.ts": 26, + "src/renderer/src/lib/floating-workspace-tour-interaction-snapshot.test.ts": 9, + "src/renderer/src/lib/focus-terminal-tab-surface.test.ts": 16, + "src/renderer/src/lib/folder-workspace-path-status.test.ts": 8, + "src/renderer/src/lib/foreground-terminal-tabs.test.ts": 14, + "src/renderer/src/lib/github-links.test.ts": 11, + "src/renderer/src/lib/github-pr-start-point.test.ts": 11, + "src/renderer/src/lib/github-source-runtime-context.test.ts": 8, + "src/renderer/src/lib/github-work-item-source-lookup.test.ts": 8, + "src/renderer/src/lib/github-work-item-workspace-attachment.test.ts": 10, + "src/renderer/src/lib/gitlab-links.test.ts": 14, + "src/renderer/src/lib/gitlab-work-item-source-lookup.test.ts": 8, + "src/renderer/src/lib/hook-command-delayed-delivery-perf.test.ts": 121, + "src/renderer/src/lib/hook-command-delayed-delivery.test.ts": 11, + "src/renderer/src/lib/host-mirrored-pane-resume-replay.test.ts": 20, + "src/renderer/src/lib/http-link-destinations.test.ts": 14, + "src/renderer/src/lib/http-link-modifier-routing.test.ts": 12, + "src/renderer/src/lib/http-link-routing.test.ts": 25, + "src/renderer/src/lib/i18n-jsx-spacing-guard.test.ts": 23, + "src/renderer/src/lib/ime-composition-keyboard-event.test.ts": 30, + "src/renderer/src/lib/ios-web-platform.test.ts": 6, + "src/renderer/src/lib/jira-source-host.test.ts": 8, + "src/renderer/src/lib/keyboard-layout/detect-option-as-alt.test.ts": 11, + "src/renderer/src/lib/keyboard-layout/input-source-id.test.ts": 4, + "src/renderer/src/lib/keyboard-layout/layout-base-character.test.ts": 14, + "src/renderer/src/lib/keyboard-layout/option-as-alt-probe.test.ts": 20, + "src/renderer/src/lib/keyboard-layout/option-key-location-state.test.ts": 7, + "src/renderer/src/lib/language-detect.test.ts": 8, + "src/renderer/src/lib/large-text-control-paste.test.ts": 23, + "src/renderer/src/lib/launch-agent-background-session-remote.test.ts": 498, + "src/renderer/src/lib/launch-agent-background-session.test.ts": 360, + "src/renderer/src/lib/launch-agent-in-new-tab-cwd.test.ts": 653, + "src/renderer/src/lib/launch-agent-in-new-tab-host-resolution.test.ts": 553, + "src/renderer/src/lib/launch-agent-in-new-tab-structured.test.ts": 15, + "src/renderer/src/lib/launch-agent-in-new-tab-web-runtime.test.ts": 544, + "src/renderer/src/lib/launch-agent-in-new-tab-windows-quoting.test.ts": 760, + "src/renderer/src/lib/launch-agent-in-new-tab.test.ts": 1687, + "src/renderer/src/lib/launch-agent-session-continuation.test.ts": 27, + "src/renderer/src/lib/launch-agent-structured-chat-guard.test.ts": 945, + "src/renderer/src/lib/launch-ai-vault-session.test.ts": 10, + "src/renderer/src/lib/launch-structured-agent-session.test.ts": 726, + "src/renderer/src/lib/launch-work-item-direct-agent-routing.test.ts": 12, + "src/renderer/src/lib/launch-work-item-direct-agent.test.ts": 15, + "src/renderer/src/lib/launch-work-item-direct-messages.test.ts": 6, + "src/renderer/src/lib/launch-work-item-direct.test.ts": 51, + "src/renderer/src/lib/launch-worktree-background-terminals.test.ts": 305, + "src/renderer/src/lib/lazy-chunk-recovery-reload.test.ts": 61, + "src/renderer/src/lib/lazy-with-retry.never-landed-reload.test.ts": 10, + "src/renderer/src/lib/lazy-with-retry.right-sidebar-syntax-error.test.ts": 10, + "src/renderer/src/lib/lazy-with-retry.test.ts": 37, + "src/renderer/src/lib/left-sidebar-appearance.test.ts": 13, + "src/renderer/src/lib/linear-agent-skill-update-command.test.ts": 4, + "src/renderer/src/lib/linear-board-drag-payload.test.ts": 10, + "src/renderer/src/lib/linear-issue-context-snapshot.test.ts": 19, + "src/renderer/src/lib/linear-issue-url-lookup.test.ts": 10, + "src/renderer/src/lib/linear-issue-workspace-attachment.test.ts": 10, + "src/renderer/src/lib/linear-issue-workspace-open.test.ts": 10, + "src/renderer/src/lib/linear-linked-work-item.test.ts": 12, + "src/renderer/src/lib/linked-work-item-context.test.ts": 16, + "src/renderer/src/lib/linked-work-item-provider.test.ts": 5, + "src/renderer/src/lib/list-row-interaction.test.ts": 11, + "src/renderer/src/lib/local-path-open-guard.test.ts": 5, + "src/renderer/src/lib/local-preflight-context-cache.test.ts": 31, + "src/renderer/src/lib/local-preflight-context.test.ts": 18, + "src/renderer/src/lib/locale-text-collators.test.ts": 30, + "src/renderer/src/lib/manual-terminal-worktree-parking.test.ts": 7, + "src/renderer/src/lib/markdown-comment-blocks.test.ts": 6, + "src/renderer/src/lib/markdown-document-templates.test.ts": 9, + "src/renderer/src/lib/markdown-review-note-copy.test.ts": 5, + "src/renderer/src/lib/markdown-review-notes.test.ts": 26, + "src/renderer/src/lib/migration-unsupported-agent-entry.test.ts": 4, + "src/renderer/src/lib/mobile-terminal-tab-mount.test.ts": 7, + "src/renderer/src/lib/monaco-delayer-cancellation-guard.test.ts": 25, + "src/renderer/src/lib/monaco-diff-editor-disposal.test.ts": 13, + "src/renderer/src/lib/monaco-languages/monarch-embed-entry-recursion.test.ts": 388, + "src/renderer/src/lib/monaco-languages/monarch-upstream-mdx-recursion.test.ts": 61, + "src/renderer/src/lib/monaco-languages/register-astro.test.ts": 32, + "src/renderer/src/lib/monaco-languages/register-jsonl.test.ts": 27, + "src/renderer/src/lib/monaco-languages/register-nim.test.ts": 22, + "src/renderer/src/lib/monaco-languages/register-svelte.test.ts": 28, + "src/renderer/src/lib/monaco-languages/register-vue.test.ts": 20, + "src/renderer/src/lib/monaco-languages/textmate-language-registration.test.ts": 10, + "src/renderer/src/lib/monaco-languages/textmate-token-provider.test.ts": 123, + "src/renderer/src/lib/monaco-peek-preview-options.test.ts": 12, + "src/renderer/src/lib/native-chat-initial-view-mode.test.ts": 13, + "src/renderer/src/lib/native-chat-launch-draft-mirrorability.test.ts": 12, + "src/renderer/src/lib/native-chat-transcript-readability.test.ts": 4, + "src/renderer/src/lib/nested-repo-selected-paths.test.ts": 8, + "src/renderer/src/lib/new-workspace-composer-repo.test.ts": 7, + "src/renderer/src/lib/new-workspace-create-gates.test.ts": 4, + "src/renderer/src/lib/new-workspace-enter-guard.test.ts": 18, + "src/renderer/src/lib/new-workspace-project-options.test.ts": 21, + "src/renderer/src/lib/new-workspace-ssh-gate.test.ts": 6, + "src/renderer/src/lib/new-workspace.test.ts": 4395, + "src/renderer/src/lib/non-secure-context-crypto.repro.test.ts": 11, + "src/renderer/src/lib/notes-send-agent-targets.test.ts": 30, + "src/renderer/src/lib/onboarding-project-checklist.test.ts": 6, + "src/renderer/src/lib/open-markdown-in-floating-workspace.test.ts": 7, + "src/renderer/src/lib/open-mobile-emulator-tab.test.ts": 17, + "src/renderer/src/lib/open-tab-occupant-agent.test.ts": 16, + "src/renderer/src/lib/orca-hook-trust.test.ts": 9, + "src/renderer/src/lib/orca-yaml-trust-prompt-slot-eviction.test.ts": 278, + "src/renderer/src/lib/orchestration-setup-state.test.ts": 9, + "src/renderer/src/lib/orchestration-skill-coverage.test.ts": 20, + "src/renderer/src/lib/order-empty-query-worktrees.test.ts": 30, + "src/renderer/src/lib/palette-match/cmd-j-ranking-contract.test.ts": 15, + "src/renderer/src/lib/palette-match/match-field-allocation.test.ts": 49, + "src/renderer/src/lib/palette-match/palette-match-core.test.ts": 26, + "src/renderer/src/lib/palette-match/palette-match-performance.test.ts": 9461, + "src/renderer/src/lib/palette-match/palette-ranking.test.ts": 16, + "src/renderer/src/lib/palette-repo-resolution.test.ts": 9, + "src/renderer/src/lib/palette-type-alias-match.test.ts": 9, + "src/renderer/src/lib/pane-agent-evidence.test.ts": 23, + "src/renderer/src/lib/pane-manager/browser-mobile-driver-state.test.ts": 10, + "src/renderer/src/lib/pane-manager/browser-remote-viewer-state.test.ts": 5, + "src/renderer/src/lib/pane-manager/client-hosted-browser-row-ephemerality.test.ts": 22, + "src/renderer/src/lib/pane-manager/client-hosted-browser-row-state.test.ts": 16, + "src/renderer/src/lib/pane-manager/focus-follows-mouse.test.ts": 7, + "src/renderer/src/lib/pane-manager/mobile-driver-state.test.ts": 10, + "src/renderer/src/lib/pane-manager/mobile-fit-overrides-hydration.test.ts": 5, + "src/renderer/src/lib/pane-manager/mobile-fit-overrides.test.ts": 23, + "src/renderer/src/lib/pane-manager/pane-container-listener-lifecycle.test.ts": 10, + "src/renderer/src/lib/pane-manager/pane-cursor-blink-suspension.test.ts": 318, + "src/renderer/src/lib/pane-manager/pane-divider-capture-loss.test.ts": 6, + "src/renderer/src/lib/pane-manager/pane-divider-stray-touch.test.ts": 23, + "src/renderer/src/lib/pane-manager/pane-divider.test.ts": 13, + "src/renderer/src/lib/pane-manager/pane-dom-creation.test.ts": 20, + "src/renderer/src/lib/pane-manager/pane-drag-reorder.test.ts": 17, + "src/renderer/src/lib/pane-manager/pane-drop-no-op.test.ts": 12, + "src/renderer/src/lib/pane-manager/pane-fit-resize-observer.test.ts": 16, + "src/renderer/src/lib/pane-manager/pane-fit.test.ts": 61, + "src/renderer/src/lib/pane-manager/pane-initial-fit-lifecycle.test.ts": 10, + "src/renderer/src/lib/pane-manager/pane-key-resolution.test.ts": 9, + "src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts": 52, + "src/renderer/src/lib/pane-manager/pane-manager-registry.test.ts": 26, + "src/renderer/src/lib/pane-manager/pane-metric-options-deferral.test.ts": 14, + "src/renderer/src/lib/pane-manager/pane-overlay-focus.test.ts": 44, + "src/renderer/src/lib/pane-manager/pane-pointer-focus.test.ts": 5, + "src/renderer/src/lib/pane-manager/pane-pty-resize-hold.test.ts": 8, + "src/renderer/src/lib/pane-manager/pane-reveal-fit.test.ts": 10, + "src/renderer/src/lib/pane-manager/pane-reveal-repaint.test.ts": 53, + "src/renderer/src/lib/pane-manager/pane-scroll.test.ts": 87, + "src/renderer/src/lib/pane-manager/pane-split-close.test.ts": 12, + "src/renderer/src/lib/pane-manager/pane-split-scroll.test.ts": 12, + "src/renderer/src/lib/pane-manager/pane-terminal-foreground-render-settle.test.ts": 8, + "src/renderer/src/lib/pane-manager/pane-terminal-gpu-acceleration.test.ts": 12, + "src/renderer/src/lib/pane-manager/pane-terminal-mouse-wheel.test.ts": 15, + "src/renderer/src/lib/pane-manager/pane-terminal-output-queue-chunks.test.ts": 5, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler-ack-credit.test.ts": 69, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler-backlog-cap.test.ts": 110, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler-disposed-writes.test.ts": 46, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler-foreground-refresh.test.ts": 68, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler-queue-retention.test.ts": 254, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler-synchronized-frames.test.ts": 65, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler-throughput.bench.test.ts": 256, + "src/renderer/src/lib/pane-manager/pane-terminal-output-scheduler.test.ts": 146, + "src/renderer/src/lib/pane-manager/pane-tree-equalization-parity.test.ts": 1130, + "src/renderer/src/lib/pane-manager/pane-tree-equalization-scaling.test.ts": 65, + "src/renderer/src/lib/pane-manager/pane-tree-ops.test.ts": 26, + "src/renderer/src/lib/pane-manager/pane-tree-reparent-frame.test.ts": 30, + "src/renderer/src/lib/pane-manager/pane-webgl-context-recovery.test.ts": 50, + "src/renderer/src/lib/pane-manager/pane-webgl-refresh-lifecycle.test.ts": 8, + "src/renderer/src/lib/pane-manager/pane-webgl-renderer.test.ts": 38, + "src/renderer/src/lib/pane-manager/terminal-arabic-shaping-joiner.test.ts": 16, + "src/renderer/src/lib/pane-manager/terminal-canvas-dpr-repair.test.ts": 11, + "src/renderer/src/lib/pane-manager/terminal-complex-script.test.ts": 15, + "src/renderer/src/lib/pane-manager/terminal-delivery-credit.test.ts": 12, + "src/renderer/src/lib/pane-manager/terminal-foreground-repair-convergence.test.ts": 183, + "src/renderer/src/lib/pane-manager/terminal-ime-anchor.test.ts": 11, + "src/renderer/src/lib/pane-manager/terminal-ime-candidate-anchor.test.ts": 41, + "src/renderer/src/lib/pane-manager/terminal-keyboard-protocol.test.ts": 12, + "src/renderer/src/lib/pane-manager/terminal-ligatures-addon.test.ts": 16, + "src/renderer/src/lib/pane-manager/terminal-link-provider-guard.test.ts": 12, + "src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset-on-mouseleave.test.ts": 14, + "src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset-on-write.test.ts": 13, + "src/renderer/src/lib/pane-manager/terminal-linkifier-hover-reset.test.ts": 5, + "src/renderer/src/lib/pane-manager/terminal-render-pause-release-parked-resize.test.ts": 10, + "src/renderer/src/lib/pane-manager/terminal-render-pause-release.test.ts": 12, + "src/renderer/src/lib/pane-manager/terminal-scroll-intent-input-resync.test.ts": 14, + "src/renderer/src/lib/pane-manager/terminal-scroll-intent-key-retention.test.ts": 55, + "src/renderer/src/lib/pane-manager/terminal-scroll-intent-structural-transitions.test.ts": 13, + "src/renderer/src/lib/pane-manager/terminal-scroll-intent.test.ts": 22, + "src/renderer/src/lib/pane-manager/terminal-structural-replay-coordinator.test.ts": 16, + "src/renderer/src/lib/pane-manager/terminal-webgl-auto-policy.test.ts": 13, + "src/renderer/src/lib/pane-manager/terminal-webgl-hidden-retention.test.ts": 19, + "src/renderer/src/lib/pane-manager/terminal-windows-ctrl-alt-chord-classification.test.ts": 16, + "src/renderer/src/lib/pane-manager/terminal-write-pipeline-health.test.ts": 19, + "src/renderer/src/lib/pane-manager/windows-pty-compatibility.test.ts": 20, + "src/renderer/src/lib/pane-manager/xterm-instance-disposed.test.ts": 10, + "src/renderer/src/lib/pane-manager/xterm-user-scrolling-contract.test.ts": 5037, + "src/renderer/src/lib/pane-manager/xterm-write-callback-guard.test.ts": 10, + "src/renderer/src/lib/parked-terminal-host-hydration.test.ts": 12, + "src/renderer/src/lib/passive-macos-app-data-access.test.ts": 6, + "src/renderer/src/lib/paste-payload-metadata.test.ts": 11, + "src/renderer/src/lib/path-head-elision.test.ts": 5, + "src/renderer/src/lib/path.test.ts": 6, + "src/renderer/src/lib/pending-worktree-creation.test.ts": 5, + "src/renderer/src/lib/pi-live-session-no-duplicate-tab.test.ts": 11, + "src/renderer/src/lib/pi-session-resume-wake.test.ts": 12, + "src/renderer/src/lib/plugin-command-execution.test.ts": 7, + "src/renderer/src/lib/plugin-command-keybindings.test.ts": 19, + "src/renderer/src/lib/pr-bot-author-overrides.test.ts": 213, + "src/renderer/src/lib/pr-comment-action-state.test.ts": 12, + "src/renderer/src/lib/pr-comment-reactions.test.ts": 6, + "src/renderer/src/lib/primary-selection-paste.test.ts": 79, + "src/renderer/src/lib/primary-selection.test.ts": 9, + "src/renderer/src/lib/project-clone-url-prefill.test.ts": 15, + "src/renderer/src/lib/project-host-clone-url.test.ts": 8, + "src/renderer/src/lib/project-host-setup-options.test.ts": 20, + "src/renderer/src/lib/project-host-workspace-target.test.ts": 17, + "src/renderer/src/lib/project-skill-runtime.test.ts": 8, + "src/renderer/src/lib/provisioned-root-create-options.test.ts": 8, + "src/renderer/src/lib/quick-workspace-agent-selection.test.ts": 7, + "src/renderer/src/lib/react-commit-cascade-install-order.test.ts": 8, + "src/renderer/src/lib/react-commit-cascade-observer.react185.test.tsx": 44, + "src/renderer/src/lib/react-commit-cascade-observer.test.ts": 150, + "src/renderer/src/lib/react-commit-cascade-store-write-samples.test.ts": 12, + "src/renderer/src/lib/react-commit-cascade-telemetry.test.ts": 108, + "src/renderer/src/lib/react-error-boundary-reporting.test.ts": 15, + "src/renderer/src/lib/react-grab-dev-gate.test.ts": 4, + "src/renderer/src/lib/react-renderer-root.test.ts": 6, + "src/renderer/src/lib/recent-workspace-tab-rows.test.ts": 19, + "src/renderer/src/lib/remap-open-editor-tabs-for-path-change.test.ts": 62, + "src/renderer/src/lib/renderer-agent-status-observation-ingress.test.ts": 36, + "src/renderer/src/lib/renderer-app-platform.test.ts": 34, + "src/renderer/src/lib/renderer-memory-profile.test.ts": 26, + "src/renderer/src/lib/repo-display-labels.test.ts": 6, + "src/renderer/src/lib/repo-runtime-owner.test.ts": 14, + "src/renderer/src/lib/repo-search.test.ts": 10, + "src/renderer/src/lib/repo-slug-cache.test.ts": 8, + "src/renderer/src/lib/repo-slug-index.test.ts": 515, + "src/renderer/src/lib/resolved-worktree-execution-host.test.ts": 7, + "src/renderer/src/lib/resume-sleeping-agent-session-direct-ssh-hydration-gap.test.ts": 22, + "src/renderer/src/lib/resume-sleeping-agent-session-provider-claim.test.ts": 13, + "src/renderer/src/lib/resume-sleeping-agent-session-remote-compat.test.ts": 25, + "src/renderer/src/lib/resume-sleeping-agent-session-replay.test.ts": 65, + "src/renderer/src/lib/resume-sleeping-agent-session-slept-pane-recovery.test.ts": 11, + "src/renderer/src/lib/resume-sleeping-agent-session-suppress-navigation.test.ts": 32, + "src/renderer/src/lib/resume-sleeping-agent-session.test.ts": 126, + "src/renderer/src/lib/resume-stale-structured-agent-session.test.ts": 9, + "src/renderer/src/lib/right-sidebar-visibility.test.ts": 8, + "src/renderer/src/lib/run-quick-command-in-new-tab.test.ts": 10, + "src/renderer/src/lib/running-agent-targets.test.ts": 12, + "src/renderer/src/lib/runtime-pane-title-leaf-id.test.ts": 9, + "src/renderer/src/lib/runtime-session-mirror-targets.test.ts": 6, + "src/renderer/src/lib/runtime-workspace-file-route.test.ts": 14, + "src/renderer/src/lib/screen-submit-shortcut.test.ts": 6, + "src/renderer/src/lib/script-textarea-rows.test.ts": 52, + "src/renderer/src/lib/scroll-cache.test.ts": 9, + "src/renderer/src/lib/serve-desktop-promotion-session-continuity.test.ts": 13, + "src/renderer/src/lib/session-write-subscriber-allocation.test.ts": 21, + "src/renderer/src/lib/session-write-subscriber-deferred-persist.test.ts": 21, + "src/renderer/src/lib/session-write-subscriber.test.ts": 107, + "src/renderer/src/lib/settled-worker-wake-policy.test.ts": 25, + "src/renderer/src/lib/setup-runner.test.ts": 4, + "src/renderer/src/lib/setup-script-prompt.test.ts": 20, + "src/renderer/src/lib/sha256.test.ts": 22, + "src/renderer/src/lib/shutdown-checkpoint-guard.test.ts": 26, + "src/renderer/src/lib/sidebar-worktree-activation.test.ts": 13, + "src/renderer/src/lib/simulator-launch-coordination.test.ts": 112, + "src/renderer/src/lib/simulator-palette-search.test.ts": 29, + "src/renderer/src/lib/simulator-pane-shutdown-scheduler.test.ts": 17, + "src/renderer/src/lib/simulator-tab-palette-activation.test.ts": 32, + "src/renderer/src/lib/skill-freshness-display-status.test.ts": 16, + "src/renderer/src/lib/sleeping-agent-session-launch-windows-quoting.test.ts": 290, + "src/renderer/src/lib/smart-github-submit.test.ts": 19, + "src/renderer/src/lib/source-control-agent-action-plan.test.ts": 15, + "src/renderer/src/lib/source-control-generation-plan.test.ts": 16, + "src/renderer/src/lib/source-control-launch-agent-selection.test.ts": 12, + "src/renderer/src/lib/source-control-launch-platform.test.ts": 10, + "src/renderer/src/lib/source-control-remote-error.test.ts": 27, + "src/renderer/src/lib/sparse-preset-draft.test.ts": 21, + "src/renderer/src/lib/ssh-background-startup-delivery.test.ts": 17, + "src/renderer/src/lib/ssh-mutation-expectation.test.ts": 7, + "src/renderer/src/lib/startup-ui-hydration.test.ts": 10, + "src/renderer/src/lib/state-collection-byte-estimate.test.ts": 46, + "src/renderer/src/lib/structured-agent-launch-settlement-caller-census.test.ts": 361, + "src/renderer/src/lib/structured-agent-launch-settlement.test.ts": 67, + "src/renderer/src/lib/structured-agent-session-launch-join-delivery.test.ts": 8, + "src/renderer/src/lib/structured-agent-session-launch-prompt.test.ts": 12, + "src/renderer/src/lib/structured-agent-session-launch-refusal-fallback.test.ts": 25, + "src/renderer/src/lib/structured-agent-session-launch-resume-identity.test.ts": 16, + "src/renderer/src/lib/structured-agent-session-launch.test.ts": 307, + "src/renderer/src/lib/structured-agent-session-tab-activation.test.ts": 7, + "src/renderer/src/lib/tab-agent-identity-decision-table.test.ts": 438, + "src/renderer/src/lib/tab-agent-status-index.test.ts": 162, + "src/renderer/src/lib/tab-agent.test.ts": 10, + "src/renderer/src/lib/tab-has-live-pty.test.ts": 7, + "src/renderer/src/lib/tab-number-shortcuts.test.ts": 9, + "src/renderer/src/lib/terminal-contrast-correction.test.ts": 13, + "src/renderer/src/lib/terminal-file-uri-link.test.ts": 8, + "src/renderer/src/lib/terminal-input-activity-coalescing.test.ts": 12, + "src/renderer/src/lib/terminal-links-redos.test.ts": 12, + "src/renderer/src/lib/terminal-links.test.ts": 331, + "src/renderer/src/lib/terminal-pane-title-sanitization.test.ts": 7, + "src/renderer/src/lib/terminal-quick-command-project-scope.test.ts": 7, + "src/renderer/src/lib/terminal-quick-command-search.test.ts": 11, + "src/renderer/src/lib/terminal-reveal-identity.test.ts": 10, + "src/renderer/src/lib/terminal-shortcut-capture-notification.test.tsx": 16, + "src/renderer/src/lib/terminal-tab-for-pty-id.test.ts": 10, + "src/renderer/src/lib/terminal-theme.test.ts": 22, + "src/renderer/src/lib/terminal-worktree-route.test.ts": 16, + "src/renderer/src/lib/text-control-paste-ownership.test.ts": 21, + "src/renderer/src/lib/text-control-paste.test.ts": 30, + "src/renderer/src/lib/titlebar-left-chrome.test.ts": 6, + "src/renderer/src/lib/titlebar-worktree-history-controls.test.ts": 4, + "src/renderer/src/lib/tui-agent-startup.test.ts": 18, + "src/renderer/src/lib/typing-latency/diagnostic-lifecycle.test.ts": 14, + "src/renderer/src/lib/typing-latency/diagnostic-summary.test.ts": 13, + "src/renderer/src/lib/typing-latency/echo-instrumentation.test.ts": 28, + "src/renderer/src/lib/typing-latency/input-events.test.ts": 7, + "src/renderer/src/lib/typing-latency/input-source.test.ts": 8, + "src/renderer/src/lib/typing-latency/sample-window.test.ts": 10, + "src/renderer/src/lib/unread-badge-count.test.ts": 4, + "src/renderer/src/lib/update-check-click-options.test.ts": 5, + "src/renderer/src/lib/updater-beforeunload.test.ts": 8, + "src/renderer/src/lib/use-tab-agent-observed-signal-dispatch.test.tsx": 63, + "src/renderer/src/lib/use-tab-agent-opencode-native-title.test.ts": 40, + "src/renderer/src/lib/use-tab-agent-pi-identity.test.ts": 27, + "src/renderer/src/lib/use-tab-agent-process-signals.test.ts": 48, + "src/renderer/src/lib/use-tab-agent-remote-pty-selector.test.ts": 30, + "src/renderer/src/lib/use-tab-agent-retained-identity.test.ts": 36, + "src/renderer/src/lib/use-tab-agent-sleeping-session.test.ts": 37, + "src/renderer/src/lib/use-tab-agent.test.ts": 112, + "src/renderer/src/lib/visible-overlay.test.ts": 15, + "src/renderer/src/lib/wake-sleeping-agents-in-background.test.ts": 19, + "src/renderer/src/lib/wake-sleeping-agents-live-done.test.ts": 10, + "src/renderer/src/lib/web-client-location.test.ts": 6, + "src/renderer/src/lib/window-label-formatter.test.ts": 13, + "src/renderer/src/lib/window-visibility-interval.test.ts": 15, + "src/renderer/src/lib/window-visibility-timeout-poller.test.ts": 10, + "src/renderer/src/lib/windows-terminal-capabilities-race.test.ts": 7, + "src/renderer/src/lib/windows-terminal-capabilities.test.ts": 64, + "src/renderer/src/lib/windows-terminal-capability-reprobe.test.ts": 25, + "src/renderer/src/lib/work-item-link-query-bounds.test.ts": 5, + "src/renderer/src/lib/work-item-lookup-text.test.ts": 8, + "src/renderer/src/lib/worker-terminal-takeover-report.test.ts": 257, + "src/renderer/src/lib/workspace-activation-path-gate.test.ts": 23, + "src/renderer/src/lib/workspace-activation-terminal-focus.test.ts": 270, + "src/renderer/src/lib/workspace-browser-tab-open.test.ts": 37, + "src/renderer/src/lib/workspace-composer-initial-focus.test.ts": 16, + "src/renderer/src/lib/workspace-create-error-format.test.ts": 9, + "src/renderer/src/lib/workspace-doc-address-input.test.ts": 12, + "src/renderer/src/lib/workspace-emoji-shortcodes.lazy.test.ts": 60, + "src/renderer/src/lib/workspace-emoji-shortcodes.test.ts": 29, + "src/renderer/src/lib/workspace-file-drag.test.ts": 7, + "src/renderer/src/lib/workspace-port-groups.test.ts": 5, + "src/renderer/src/lib/workspace-port-host-availability.test.ts": 12, + "src/renderer/src/lib/workspace-port-scan-debounce.test.ts": 12, + "src/renderer/src/lib/workspace-port-scan-publish.test.ts": 24, + "src/renderer/src/lib/workspace-session-browser-history.test.ts": 12, + "src/renderer/src/lib/workspace-session-closed-tab-tombstones.test.ts": 7, + "src/renderer/src/lib/workspace-session-editor-drafts.test.ts": 9, + "src/renderer/src/lib/workspace-session-host-contention.test.ts": 18, + "src/renderer/src/lib/workspace-session-host-persistence.test.ts": 22, + "src/renderer/src/lib/workspace-session-host-split.test.ts": 14, + "src/renderer/src/lib/workspace-session-hydration-keys.test.ts": 8, + "src/renderer/src/lib/workspace-session-liveness.test.ts": 8, + "src/renderer/src/lib/workspace-session-patch.test.ts": 12, + "src/renderer/src/lib/workspace-session-persistence-gate.test.ts": 8, + "src/renderer/src/lib/workspace-session-relevant-fields.test.ts": 6, + "src/renderer/src/lib/workspace-session-staged-browser-tabs.test.ts": 5, + "src/renderer/src/lib/workspace-session.test.ts": 20, + "src/renderer/src/lib/workspace-tab-agent-metadata.test.ts": 11, + "src/renderer/src/lib/workspace-tab-palette-activation.store.test.ts": 10, + "src/renderer/src/lib/workspace-tab-palette-activation.test.ts": 19, + "src/renderer/src/lib/workspace-tab-palette-results.test.ts": 19, + "src/renderer/src/lib/workspace-tab-palette-search.test.ts": 37, + "src/renderer/src/lib/workspace-terminal-host-authority.test.ts": 38, + "src/renderer/src/lib/worktree-activation-agent-startup.test.ts": 42, + "src/renderer/src/lib/worktree-activation-automation-filter.test.ts": 19, + "src/renderer/src/lib/worktree-activation-created-agent.test.ts": 72, + "src/renderer/src/lib/worktree-activation-default-tabs.test.ts": 33, + "src/renderer/src/lib/worktree-activation-emptied-workspace-reseed.test.ts": 71, + "src/renderer/src/lib/worktree-activation-empty-remote.test.ts": 63, + "src/renderer/src/lib/worktree-activation-issue-command.test.ts": 21, + "src/renderer/src/lib/worktree-activation-pty-inventory.test.ts": 18, + "src/renderer/src/lib/worktree-activation-reveal.test.ts": 18, + "src/renderer/src/lib/worktree-activation-setup-script.test.ts": 67, + "src/renderer/src/lib/worktree-activation-structured-chat-surface.test.ts": 43, + "src/renderer/src/lib/worktree-activation-surface-caller-wiring.test.ts": 366, + "src/renderer/src/lib/worktree-activation-web-runtime.test.ts": 198, + "src/renderer/src/lib/worktree-activity-state.test.ts": 8, + "src/renderer/src/lib/worktree-agent-activation-gate.test.ts": 36, + "src/renderer/src/lib/worktree-agent-activation-seam.test.ts": 37, + "src/renderer/src/lib/worktree-agent-structured-inventory.test.ts": 8, + "src/renderer/src/lib/worktree-attachment-label.test.ts": 6, + "src/renderer/src/lib/worktree-creation-agent-seeding.test.ts": 11, + "src/renderer/src/lib/worktree-creation-agent-seeds.test.ts": 107, + "src/renderer/src/lib/worktree-creation-chat-setup.test.ts": 23, + "src/renderer/src/lib/worktree-creation-flow-agent-trust-preflight.test.ts": 3, + "src/renderer/src/lib/worktree-creation-flow-dedupe.test.ts": 7, + "src/renderer/src/lib/worktree-creation-flow-stranded-surface.test.ts": 123, + "src/renderer/src/lib/worktree-creation-flow.test.ts": 879, + "src/renderer/src/lib/worktree-creation-structured-session.test.ts": 28, + "src/renderer/src/lib/worktree-creation-structured-unknown-outcome.test.ts": 9, + "src/renderer/src/lib/worktree-creation-surface.test.ts": 4, + "src/renderer/src/lib/worktree-default-display-name.test.ts": 11, + "src/renderer/src/lib/worktree-display-name-order.test.ts": 13, + "src/renderer/src/lib/worktree-draft-startup-view-mode.test.ts": 12, + "src/renderer/src/lib/worktree-git-identity-display.test.ts": 4, + "src/renderer/src/lib/worktree-jump-navigation.test.ts": 21, + "src/renderer/src/lib/worktree-live-terminal-surface-owners.test.ts": 13, + "src/renderer/src/lib/worktree-operation-generation.test.ts": 7, + "src/renderer/src/lib/worktree-operation-route.test.ts": 20, + "src/renderer/src/lib/worktree-palette-comment-snippet.test.ts": 13, + "src/renderer/src/lib/worktree-palette-create-action.test.ts": 7, + "src/renderer/src/lib/worktree-palette-multi-keyword.test.ts": 143, + "src/renderer/src/lib/worktree-palette-review-match.test.ts": 9, + "src/renderer/src/lib/worktree-palette-runtime-owner-identity.test.ts": 14, + "src/renderer/src/lib/worktree-palette-search.test.ts": 62, + "src/renderer/src/lib/worktree-palette-task-url-match.test.ts": 25, + "src/renderer/src/lib/worktree-reactivation-preserved-pane-replacement.test.ts": 53, + "src/renderer/src/lib/worktree-reactivation-runtime-owned-resume-deferral.test.ts": 30, + "src/renderer/src/lib/worktree-reactivation-tab-forkbomb.test.ts": 30, + "src/renderer/src/lib/worktree-runtime-owner-index.detected-perf.test.ts": 411, + "src/renderer/src/lib/worktree-runtime-owner-index.detected.test.ts": 80, + "src/renderer/src/lib/worktree-runtime-owner-index.test.ts": 7, + "src/renderer/src/lib/worktree-runtime-owner.test.ts": 64, + "src/renderer/src/lib/worktree-sort-order-host-split.test.ts": 5, + "src/renderer/src/lib/worktree-sort-order-persistence.test.ts": 29, + "src/renderer/src/lib/worktree-status-spinner-launch-agent.test.ts": 15, + "src/renderer/src/lib/worktree-status-terminal-layout-roots.test.ts": 4, + "src/renderer/src/lib/worktree-status.interrupted.test.ts": 8, + "src/renderer/src/lib/worktree-status.test.ts": 16, + "src/renderer/src/lib/worktree-visit-recency.test.ts": 6, + "src/renderer/src/renderer-node-builtin-boundary.test.ts": 1808, + "src/renderer/src/runtime/agent-resume-host-authority-capability.test.ts": 11, + "src/renderer/src/runtime/agent-session-operation-id.test.ts": 6, + "src/renderer/src/runtime/browser-client-host-identity.test.ts": 7, + "src/renderer/src/runtime/browser-workspace-tab-close-census.test.ts": 221, + "src/renderer/src/runtime/browser-workspace-tab-close-plan.test.ts": 11, + "src/renderer/src/runtime/browser-workspace-tab-close.test.ts": 21, + "src/renderer/src/runtime/client-hosted-browser-close-intent-replay.test.ts": 11, + "src/renderer/src/runtime/client-hosted-browser-close-intents.test.ts": 16, + "src/renderer/src/runtime/client-hosted-browser-row-close.test.ts": 13, + "src/renderer/src/runtime/close-mirrored-editor-tab.test.ts": 61, + "src/renderer/src/runtime/file-explorer-delete-owner-provenance.test.ts": 47, + "src/renderer/src/runtime/focus-runtime-terminal-surface-chat-view.test.ts": 13, + "src/renderer/src/runtime/github-check-details-timeout.test.ts": 14, + "src/renderer/src/runtime/gitlab-job-trace-client.test.ts": 25, + "src/renderer/src/runtime/host-session-mirror-empty-inventory-settle.test.ts": 37, + "src/renderer/src/runtime/host-session-mirror-hydration-frame-ordering.test.tsx": 120, + "src/renderer/src/runtime/host-session-mirror-settle-census.test.ts": 76, + "src/renderer/src/runtime/host-session-mirror-settle-receipt-frames.test.tsx": 165, + "src/renderer/src/runtime/host-session-snapshot-authority-client-hosted.test.ts": 4, + "src/renderer/src/runtime/local-runtime-capabilities.test.ts": 10, + "src/renderer/src/runtime/local-session-tab-close-owner.test.ts": 9, + "src/renderer/src/runtime/local-structured-session-empty-worktree-visibility.test.ts": 17, + "src/renderer/src/runtime/local-structured-session-retired-epoch-repair.test.ts": 21, + "src/renderer/src/runtime/local-structured-session-reveal-visibility.test.ts": 35, + "src/renderer/src/runtime/local-structured-session-tabs-host-isolation.test.ts": 28, + "src/renderer/src/runtime/local-structured-session-tabs-sync.test.ts": 210, + "src/renderer/src/runtime/local-structured-session-tabs-sync/retired-epoch-repair.test.ts": 16, + "src/renderer/src/runtime/mirrored-agent-status-clock-skew.test.ts": 51, + "src/renderer/src/runtime/mobile-markdown-bridge-save-guards.test.ts": 58, + "src/renderer/src/runtime/mobile-markdown-bridge.test.ts": 31, + "src/renderer/src/runtime/native-chat-launch-draft-runtime-resolution.test.ts": 6, + "src/renderer/src/runtime/paired-reconnect-sidebar-agent-count.test.ts": 146, + "src/renderer/src/runtime/remote-agent-row-last-assistant-message.test.ts": 98, + "src/renderer/src/runtime/remote-agent-session-launch.test.ts": 11, + "src/renderer/src/runtime/remote-host-file-delete-repro.test.ts": 30, + "src/renderer/src/runtime/remote-host-file-open-repro.test.ts": 16, + "src/renderer/src/runtime/remote-runtime-session-tabs-inflight.test.ts": 11, + "src/renderer/src/runtime/remote-runtime-snapshot-outcome.test.ts": 28, + "src/renderer/src/runtime/remote-runtime-terminal-end-verdict.test.ts": 49, + "src/renderer/src/runtime/remote-runtime-terminal-frame-drop-resync.test.ts": 47, + "src/renderer/src/runtime/remote-runtime-terminal-parse-backpressure.test.ts": 296, + "src/renderer/src/runtime/remote-runtime-terminal-snapshot-kitty-flags.test.ts": 17, + "src/renderer/src/runtime/remote-runtime-terminal-stale-stream-frames.test.ts": 9, + "src/renderer/src/runtime/remote-runtime-terminal-stall-recovery.test.ts": 145, + "src/renderer/src/runtime/remote-server-install-failure-probe.test.ts": 9, + "src/renderer/src/runtime/remote-server-parity.test.ts": 28, + "src/renderer/src/runtime/remote-server-restart-wait.test.ts": 7, + "src/renderer/src/runtime/remote-server-update-coordinator.test.ts": 24, + "src/renderer/src/runtime/remote-terminal-layout-resolution.test.ts": 14, + "src/renderer/src/runtime/remote-terminal-stream-watchdog.test.ts": 13, + "src/renderer/src/runtime/restored-client-hosted-browser-host-attach.test.ts": 16, + "src/renderer/src/runtime/restored-client-hosted-browser-host-restart-attach.test.ts": 15, + "src/renderer/src/runtime/runtime-client-events.test.ts": 9, + "src/renderer/src/runtime/runtime-environment-ssh-state.test.ts": 148, + "src/renderer/src/runtime/runtime-file-client-download.test.ts": 21, + "src/renderer/src/runtime/runtime-file-client-external-import.test.ts": 20, + "src/renderer/src/runtime/runtime-file-client-mutation-ownership.test.ts": 17, + "src/renderer/src/runtime/runtime-file-client-search-listing.test.ts": 129, + "src/renderer/src/runtime/runtime-file-client-watch.test.ts": 73, + "src/renderer/src/runtime/runtime-file-client.test.ts": 18, + "src/renderer/src/runtime/runtime-file-import-pairing-revision.test.ts": 22, + "src/renderer/src/runtime/runtime-file-search-bounds.test.ts": 7, + "src/renderer/src/runtime/runtime-git-client-api-contract.test.ts": 4, + "src/renderer/src/runtime/runtime-git-client-branch-line-total.test.ts": 13, + "src/renderer/src/runtime/runtime-git-client-merge.test.ts": 11, + "src/renderer/src/runtime/runtime-git-client.test.ts": 32, + "src/renderer/src/runtime/runtime-hooks-client.test.ts": 9, + "src/renderer/src/runtime/runtime-host-connection-state.test.ts": 7, + "src/renderer/src/runtime/runtime-jira-client.test.ts": 17, + "src/renderer/src/runtime/runtime-jira-payload-stream.test.ts": 10, + "src/renderer/src/runtime/runtime-linear-client.test.ts": 25, + "src/renderer/src/runtime/runtime-provider-accounts-client.test.ts": 37, + "src/renderer/src/runtime/runtime-provider-search-bounds.test.ts": 7, + "src/renderer/src/runtime/runtime-repo-client.test.ts": 7, + "src/renderer/src/runtime/runtime-repo-search-bounds.test.ts": 7, + "src/renderer/src/runtime/runtime-rpc-client-pairing-revision.test.ts": 7, + "src/renderer/src/runtime/runtime-rpc-client.test.ts": 22, + "src/renderer/src/runtime/runtime-rpc-result.test.ts": 9, + "src/renderer/src/runtime/runtime-server-directory-browser.test.ts": 9, + "src/renderer/src/runtime/runtime-skills-client.test.ts": 12, + "src/renderer/src/runtime/runtime-skills-delete-client.test.ts": 15, + "src/renderer/src/runtime/runtime-terminal-inspection.test.ts": 683, + "src/renderer/src/runtime/runtime-terminal-stream.test.ts": 286, + "src/renderer/src/runtime/runtime-worktree-selector.test.ts": 6, + "src/renderer/src/runtime/structured-agent-session-client.test.ts": 14, + "src/renderer/src/runtime/structured-agent-session-close.test.ts": 8, + "src/renderer/src/runtime/structured-agent-session-status-feed-lifecycle.test.ts": 11, + "src/renderer/src/runtime/structured-agent-session-status-feed.test.ts": 18, + "src/renderer/src/runtime/structured-conversation-tab-replacement.test.ts": 20, + "src/renderer/src/runtime/sync-runtime-graph-agent-status-projection-join.test.ts": 51, + "src/renderer/src/runtime/sync-runtime-graph-agent-status-projection.test.ts": 20, + "src/renderer/src/runtime/sync-runtime-graph-automation-leaf.test.ts": 16, + "src/renderer/src/runtime/sync-runtime-graph-browser.test.ts": 15, + "src/renderer/src/runtime/sync-runtime-graph-conversion-publish.test.ts": 83, + "src/renderer/src/runtime/sync-runtime-graph-editor-diff-tabs.test.ts": 19, + "src/renderer/src/runtime/sync-runtime-graph-key-reuse.test.ts": 19, + "src/renderer/src/runtime/sync-runtime-graph-parked-leaf.test.ts": 29, + "src/renderer/src/runtime/sync-runtime-graph-payload-partition.test.ts": 32, + "src/renderer/src/runtime/sync-runtime-graph-projection-hot-path.test.ts": 128, + "src/renderer/src/runtime/sync-runtime-graph-publication-cost.test.ts": 55, + "src/renderer/src/runtime/sync-runtime-graph-scheduling.test.ts": 24, + "src/renderer/src/runtime/sync-runtime-graph-terminal-layout.test.ts": 696, + "src/renderer/src/runtime/sync-runtime-graph-terminal-registration-ownership.test.ts": 13, + "src/renderer/src/runtime/sync-runtime-graph-terminal-surface-projection.test.ts": 30, + "src/renderer/src/runtime/sync-runtime-graph-workspace-publication.test.ts": 19, + "src/renderer/src/runtime/sync-runtime-graph.test.ts": 20, + "src/renderer/src/runtime/sync-runtime-graph/mobile-terminal-theme.test.ts": 4, + "src/renderer/src/runtime/use-remote-runtime-recovery-triggers.test.ts": 22, + "src/renderer/src/runtime/use-runtime-session-mirror-environment-key.test.ts": 78, + "src/renderer/src/runtime/use-worktree-runtime-target.test.ts": 58, + "src/renderer/src/runtime/web-runtime-browser-capability-cleanup.test.ts": 16, + "src/renderer/src/runtime/web-runtime-browser-materialization.test.ts": 4, + "src/renderer/src/runtime/web-runtime-browser-tab-staging-hosting-intent.test.ts": 9, + "src/renderer/src/runtime/web-runtime-session-browser-client-placement.test.ts": 81, + "src/renderer/src/runtime/web-runtime-session-browser-create-failure.test.ts": 112, + "src/renderer/src/runtime/web-runtime-session-browser-create-focus.test.ts": 283, + "src/renderer/src/runtime/web-runtime-session-browser-create-split-placement.test.ts": 231, + "src/renderer/src/runtime/web-runtime-session-browser-create-staged-focus.test.ts": 118, + "src/renderer/src/runtime/web-runtime-session-browser-create-staged-hosting-intent.test.ts": 32, + "src/renderer/src/runtime/web-runtime-session-browser-create-staging.test.ts": 320, + "src/renderer/src/runtime/web-runtime-session-browser-placement-staleness.test.ts": 22, + "src/renderer/src/runtime/web-runtime-session-tab-activate-close.test.ts": 27, + "src/renderer/src/runtime/web-runtime-session-tab-move.test.ts": 11, + "src/renderer/src/runtime/web-runtime-session-tab-props.test.ts": 116, + "src/renderer/src/runtime/web-runtime-session-terminal-host-authority.test.ts": 38, + "src/renderer/src/runtime/web-runtime-session-terminal-legacy-create.test.ts": 35, + "src/renderer/src/runtime/web-runtime-session-terminal-pane-delegation.test.ts": 590, + "src/renderer/src/runtime/web-runtime-session-terminal-workspace-routing.test.ts": 16, + "src/renderer/src/runtime/web-runtime-session.test.ts": 16, + "src/renderer/src/runtime/web-runtime-wake-terminal-respawn.test.ts": 4, + "src/renderer/src/runtime/web-session-browser-placement.test.ts": 8, + "src/renderer/src/runtime/web-session-close-intent.test.ts": 7, + "src/renderer/src/runtime/web-session-existing-tab-index.test.ts": 7, + "src/renderer/src/runtime/web-session-focus-intent-visible-tab.test.ts": 9, + "src/renderer/src/runtime/web-session-intent-owner.test.ts": 7, + "src/renderer/src/runtime/web-session-open-files-batch-equivalence.test.ts": 333, + "src/renderer/src/runtime/web-session-structured-tab-focus.test.ts": 11, + "src/renderer/src/runtime/web-session-tabs-agent-completion-notifications.test.ts": 40, + "src/renderer/src/runtime/web-session-tabs-sync-agent-handoff.test.ts": 15, + "src/renderer/src/runtime/web-session-tabs-sync-agent-status.test.ts": 19, + "src/renderer/src/runtime/web-session-tabs-sync-browser-tabs.test.ts": 29, + "src/renderer/src/runtime/web-session-tabs-sync-client-owned-page-content.test.ts": 22, + "src/renderer/src/runtime/web-session-tabs-sync-client-owned-placement.test.ts": 36, + "src/renderer/src/runtime/web-session-tabs-sync-diff-focus-steal.test.ts": 19, + "src/renderer/src/runtime/web-session-tabs-sync-editor-tabs.test.ts": 15, + "src/renderer/src/runtime/web-session-tabs-sync-focus-intent.test.ts": 26, + "src/renderer/src/runtime/web-session-tabs-sync-host-restart-browser-rows.test.ts": 22, + "src/renderer/src/runtime/web-session-tabs-sync-host-tab-retraction-ghost-rows.test.ts": 138, + "src/renderer/src/runtime/web-session-tabs-sync-html-preview-focus.test.ts": 21, + "src/renderer/src/runtime/web-session-tabs-sync-layout-duplicate-groups.test.ts": 15, + "src/renderer/src/runtime/web-session-tabs-sync-layout-groups.test.ts": 22, + "src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts": 54, + "src/renderer/src/runtime/web-session-tabs-sync-remote-status-title-flap.test.ts": 109, + "src/renderer/src/runtime/web-session-tabs-sync-restored-browser-rows.test.ts": 17, + "src/renderer/src/runtime/web-session-tabs-sync-snapshot-batch.test.ts": 36, + "src/renderer/src/runtime/web-session-tabs-sync-staged-browser-adoption.test.ts": 15, + "src/renderer/src/runtime/web-session-tabs-sync-staged-browser-authority.test.ts": 12, + "src/renderer/src/runtime/web-session-tabs-sync-terminal-bootstrap.test.ts": 5, + "src/renderer/src/runtime/web-session-tabs-sync-terminal-mirroring.test.ts": 30, + "src/renderer/src/runtime/web-session-tabs-sync-tracking-teardown.test.ts": 18, + "src/renderer/src/runtime/web-session-tabs-sync-visibility-collision.test.tsx": 113, + "src/renderer/src/runtime/web-session-tabs-sync-window-visibility.test.tsx": 170, + "src/renderer/src/runtime/web-session-tabs-sync.test.ts": 29, + "src/renderer/src/runtime/web-session-tabs-sync/mirrored-agent-tab-label.test.ts": 7, + "src/renderer/src/runtime/web-session-tabs-sync/mirrored-terminal-recovery-ledger.test.ts": 9, + "src/renderer/src/runtime/web-session-terminal-handle-events.test.ts": 6, + "src/renderer/src/runtime/web-session-terminal-orphan-absence-across-republication.test.ts": 11, + "src/renderer/src/runtime/web-session-terminal-orphan-inventory-retry.test.ts": 43, + "src/renderer/src/runtime/web-session-terminal-orphan-mixed-version.test.ts": 49, + "src/renderer/src/runtime/web-session-terminal-orphan-recovery-adoption-regressions.test.ts": 179, + "src/renderer/src/runtime/web-session-terminal-orphan-recovery-host-scope-gate.test.ts": 10, + "src/renderer/src/runtime/web-session-terminal-orphan-recovery-prior-removal.test.ts": 15, + "src/renderer/src/runtime/web-session-terminal-orphan-recovery-regressions.test.ts": 295, + "src/renderer/src/runtime/web-session-terminal-orphan-recovery-topology-fence.test.ts": 168, + "src/renderer/src/runtime/web-session-terminal-orphan-recovery.test.ts": 122, + "src/renderer/src/runtime/web-session-terminal-pending-handle-recovery.test.ts": 33, + "src/renderer/src/runtime/web-session-terminal-recovery-snapshot-validation.test.ts": 155, + "src/renderer/src/runtime/web-session-terminal-retirement-proof-ledger.test.ts": 11, + "src/renderer/src/runtime/window-visibility-subscription-parking.test.ts": 26, + "src/renderer/src/runtime/worktree-create-base.test.ts": 4, + "src/renderer/src/ssh/ssh-connect-in-flight.test.ts": 21, + "src/renderer/src/ssh/ssh-connect-ui-timeout.test.ts": 15, + "src/renderer/src/ssh/ssh-connect-verb.test.ts": 7, + "src/renderer/src/ssh/ssh-connection-recoverability.test.ts": 6, + "src/renderer/src/startup/active-workspace-ssh-targets.test.ts": 7, + "src/renderer/src/startup/ssh-startup-reconnect.test.ts": 16, + "src/renderer/src/startup/startup-ssh-connection-restore.test.ts": 13, + "src/renderer/src/store/active-terminal-chrome-selector.test.ts": 17, + "src/renderer/src/store/always-mounted-selector-scan-cost.test.ts": 116, + "src/renderer/src/store/copy-on-write-record.test.ts": 6, + "src/renderer/src/store/folder-workspaces/folder-workspace-catalog.test.ts": 14, + "src/renderer/src/store/pinned-tab-close-guard.test.ts": 13, + "src/renderer/src/store/plugin-language-packs.test.ts": 21, + "src/renderer/src/store/plugin-panels.test.ts": 13, + "src/renderer/src/store/project-host-setup-selector.test.ts": 18, + "src/renderer/src/store/projects/project-catalog-null-field-ingest.test.ts": 12, + "src/renderer/src/store/projects/project-wsl-filesystem-boundary-advisory.test.ts": 13, + "src/renderer/src/store/react-commit-cascade-write-probe.test.ts": 5, + "src/renderer/src/store/repos/safe-auto-fork-sync.test.ts": 8, + "src/renderer/src/store/right-sidebar-route.test.ts": 6, + "src/renderer/src/store/running-terminal-close-confirm.test.ts": 7, + "src/renderer/src/store/selectors.test.ts": 44, + "src/renderer/src/store/slices/active-tab-owner-worktree.test.ts": 12, + "src/renderer/src/store/slices/activity-cleared-at.test.ts": 82, + "src/renderer/src/store/slices/agent-generated-tab-title.test.ts": 141, + "src/renderer/src/store/slices/agent-hibernation-live-anchor-e2e.test.ts": 53, + "src/renderer/src/store/slices/agent-pane-authority.test.ts": 110, + "src/renderer/src/store/slices/agent-status-ack-cleanup.test.ts": 99, + "src/renderer/src/store/slices/agent-status-batch.test.ts": 154, + "src/renderer/src/store/slices/agent-status-drop-ipc.test.ts": 705, + "src/renderer/src/store/slices/agent-status-drop.test.ts": 88, + "src/renderer/src/store/slices/agent-status-freshness-cache.test.ts": 10, + "src/renderer/src/store/slices/agent-status-freshness-scheduler.test.ts": 20, + "src/renderer/src/store/slices/agent-status-live-freshness-request.test.ts": 7, + "src/renderer/src/store/slices/agent-status-live-map-leak.test.ts": 10291, + "src/renderer/src/store/slices/agent-status-manual-sleep-capture.test.ts": 90, + "src/renderer/src/store/slices/agent-status-observation-neutrality.test.ts": 107, + "src/renderer/src/store/slices/agent-status-pane-keyed-records.test.ts": 26, + "src/renderer/src/store/slices/agent-status-pr-refresh-handoff.test.ts": 89, + "src/renderer/src/store/slices/agent-status-provider-session.test.ts": 117, + "src/renderer/src/store/slices/agent-status-quit-capture-resumable-agents.test.ts": 64, + "src/renderer/src/store/slices/agent-status-quit-capture.test.ts": 170, + "src/renderer/src/store/slices/agent-status-reminted-pane-key.test.ts": 41, + "src/renderer/src/store/slices/agent-status-retained-leak.test.ts": 846, + "src/renderer/src/store/slices/agent-status-retention-prefix-sweep.test.ts": 85, + "src/renderer/src/store/slices/agent-status-runtime-orchestration.test.ts": 96, + "src/renderer/src/store/slices/agent-status-session-boundary-done.test.ts": 60, + "src/renderer/src/store/slices/agent-status-ssh-connection-clear.test.ts": 64, + "src/renderer/src/store/slices/agent-status-tool-assistant-fields.test.ts": 128, + "src/renderer/src/store/slices/agent-status-worktree-purge-leak.test.ts": 50, + "src/renderer/src/store/slices/agent-status.test.ts": 54, + "src/renderer/src/store/slices/ambiguous-owner-warning-worktree-removal-leak.test.ts": 587, + "src/renderer/src/store/slices/browser-cleanup-close.test.ts": 151, + "src/renderer/src/store/slices/browser-page-close-intent-recording.test.ts": 191, + "src/renderer/src/store/slices/browser-page-conversion.test.ts": 125, + "src/renderer/src/store/slices/browser-remote-page-lifecycle.test.ts": 221, + "src/renderer/src/store/slices/browser-remote-tab-creation.test.ts": 42, + "src/renderer/src/store/slices/browser-session-host-selection.test.ts": 225, + "src/renderer/src/store/slices/browser-session-profiles.test.ts": 21, + "src/renderer/src/store/slices/browser-webview-cleanup.test.ts": 7, + "src/renderer/src/store/slices/browser-workspace-doc-location.test.ts": 171, + "src/renderer/src/store/slices/browser-workspace-host-ownership.test.ts": 18, + "src/renderer/src/store/slices/browser.test.ts": 43, + "src/renderer/src/store/slices/bulk-worktree-purge-terminal-maps-leak.test.ts": 89, + "src/renderer/src/store/slices/cmd-j-create-actions.test.ts": 90, + "src/renderer/src/store/slices/codex-restart-notice-lifecycle.test.ts": 48, + "src/renderer/src/store/slices/degraded-repo-hydration.test.ts": 81, + "src/renderer/src/store/slices/detected-agents-environment-prune-leak.test.ts": 55, + "src/renderer/src/store/slices/detected-agents.test.ts": 138, + "src/renderer/src/store/slices/detected-worktree-refresh-leases.test.ts": 11, + "src/renderer/src/store/slices/dictation-model-state-stabilisation.test.ts": 9, + "src/renderer/src/store/slices/diffComments.test.ts": 122, + "src/renderer/src/store/slices/direct-ssh-pane-detach-ledger.test.ts": 62, + "src/renderer/src/store/slices/direct-ssh-terminal-recovery.test.ts": 39, + "src/renderer/src/store/slices/direct-ssh-terminal-retry.test.ts": 147, + "src/renderer/src/store/slices/direct-ssh-terminal-workspace-scope.test.ts": 11, + "src/renderer/src/store/slices/editor-branch-diff-snapshots.test.ts": 19, + "src/renderer/src/store/slices/editor-branch-line-total.test.ts": 16, + "src/renderer/src/store/slices/editor-check-details-tabs.test.ts": 27, + "src/renderer/src/store/slices/editor-close-file-cleanup.test.ts": 255, + "src/renderer/src/store/slices/editor-git-status-reconciliation.test.ts": 31, + "src/renderer/src/store/slices/editor-hydration-scaling.test.ts": 85, + "src/renderer/src/store/slices/editor-markdown-link-activation.test.ts": 38, + "src/renderer/src/store/slices/editor-markdown-view-state.test.ts": 37, + "src/renderer/src/store/slices/editor-open-diff.test.ts": 42, + "src/renderer/src/store/slices/editor-read-only-tabs.test.ts": 16, + "src/renderer/src/store/slices/editor-recently-closed-tabs.test.ts": 40, + "src/renderer/src/store/slices/editor-rekey-open-files.test.ts": 75, + "src/renderer/src/store/slices/editor-remote-branch-actions.test.ts": 62, + "src/renderer/src/store/slices/editor-rich-markdown-size-override.test.ts": 14, + "src/renderer/src/store/slices/editor-right-sidebar-state.test.ts": 24, + "src/renderer/src/store/slices/editor-session-duplicate-restore.test.ts": 84, + "src/renderer/src/store/slices/editor-state-worktree-purge-leak.test.ts": 59, + "src/renderer/src/store/slices/editor-tab-placement.test.ts": 30, + "src/renderer/src/store/slices/editor/actions/markdown-preview-actions.test.ts": 9, + "src/renderer/src/store/slices/editor/file-ids/hydrated-editor-file-index.test.ts": 512, + "src/renderer/src/store/slices/editor/file-ids/hydrated-editor-projections.test.ts": 143, + "src/renderer/src/store/slices/editor/git/git-status-reconciliation.perf.test.ts": 36, + "src/renderer/src/store/slices/folder-workspace-activation-and-activity.test.ts": 144, + "src/renderer/src/store/slices/folder-workspace-diff-comments.test.ts": 388, + "src/renderer/src/store/slices/folder-workspace-owner-routed-mutations.test.ts": 188, + "src/renderer/src/store/slices/generation-records-worktree-removal-leak.test.ts": 75, + "src/renderer/src/store/slices/github-branch-mismatched-linked-pr.test.ts": 5, + "src/renderer/src/store/slices/github-cache-eviction-and-bounds.test.ts": 221, + "src/renderer/src/store/slices/github-checks-cache.test.ts": 46, + "src/renderer/src/store/slices/github-checks.test.ts": 9, + "src/renderer/src/store/slices/github-issue-source-indicator-suppression.test.ts": 45, + "src/renderer/src/store/slices/github-issue-state-machine.test.ts": 35, + "src/renderer/src/store/slices/github-pr-branch-coordinator-events.test.ts": 35, + "src/renderer/src/store/slices/github-pr-branch-direct-refresh-scope.test.ts": 20, + "src/renderer/src/store/slices/github-pr-branch-fallback-results.test.ts": 26, + "src/renderer/src/store/slices/github-pr-branch-hosted-review-cache.test.ts": 27, + "src/renderer/src/store/slices/github-pr-branch-linked-pr-divergence.test.ts": 33, + "src/renderer/src/store/slices/github-pr-checks-fetch.test.ts": 121, + "src/renderer/src/store/slices/github-pr-comments.test.ts": 89, + "src/renderer/src/store/slices/github-pr-refresh-host-guard.test.ts": 28, + "src/renderer/src/store/slices/github-pr-refresh-hosted-review-cache-leak.test.ts": 802, + "src/renderer/src/store/slices/github-pr-refresh-owner-routing.test.ts": 218, + "src/renderer/src/store/slices/github-pr-refresh-sequences-leak.test.ts": 16, + "src/renderer/src/store/slices/github-pr-refresh-states-leak.test.ts": 4478, + "src/renderer/src/store/slices/github-project-request-coordination.test.ts": 20, + "src/renderer/src/store/slices/github-project-row-owner.test.ts": 6, + "src/renderer/src/store/slices/github-project-view-tables.test.ts": 60, + "src/renderer/src/store/slices/github-provider-request-concurrency.test.ts": 19, + "src/renderer/src/store/slices/github-refresh-sweep.test.ts": 252, + "src/renderer/src/store/slices/github-repo-lookup-index.test.ts": 4, + "src/renderer/src/store/slices/github-review-thread-actions.test.ts": 24, + "src/renderer/src/store/slices/github-work-item-cache-identity.test.ts": 13, + "src/renderer/src/store/slices/github-work-items-error-envelope.test.ts": 43, + "src/renderer/src/store/slices/github-work-items-pagination.test.ts": 29, + "src/renderer/src/store/slices/github-work-items-query-bounds.test.ts": 10, + "src/renderer/src/store/slices/github-work-items-runtime-routing.test.ts": 218, + "src/renderer/src/store/slices/github-worktree-refresh-if-stale.test.ts": 85, + "src/renderer/src/store/slices/hosted-review-cache-race.test.ts": 15, + "src/renderer/src/store/slices/hosted-review-cache.test.ts": 69, + "src/renderer/src/store/slices/hosted-review.test.ts": 20, + "src/renderer/src/store/slices/jira.test.ts": 41, + "src/renderer/src/store/slices/linear-credential-error-recovery.test.ts": 31, + "src/renderer/src/store/slices/linear-invalidation.test.ts": 23, + "src/renderer/src/store/slices/linear-issue-cache-refresh.test.ts": 17, + "src/renderer/src/store/slices/linear-scoped-collection-cache.test.ts": 40, + "src/renderer/src/store/slices/linear-source-context-cache-scope.test.ts": 20, + "src/renderer/src/store/slices/linear.test.ts": 13, + "src/renderer/src/store/slices/local-detected-agent-state.test.ts": 12, + "src/renderer/src/store/slices/memory.test.ts": 7, + "src/renderer/src/store/slices/native-chat-launch-draft-teardown.test.ts": 84, + "src/renderer/src/store/slices/new-issue-draft.test.ts": 7, + "src/renderer/src/store/slices/new-markdown.test.ts": 23, + "src/renderer/src/store/slices/orca-profiles-auth-actions.test.ts": 80, + "src/renderer/src/store/slices/orca-profiles.test.ts": 89, + "src/renderer/src/store/slices/pane-column-split-drop-no-op.test.ts": 8, + "src/renderer/src/store/slices/pane-foreground-agent.test.ts": 57, + "src/renderer/src/store/slices/persisted-ui-write-baseline.test.ts": 9, + "src/renderer/src/store/slices/pinned-tab-close-confirm.test.ts": 9, + "src/renderer/src/store/slices/preflight.test.ts": 26, + "src/renderer/src/store/slices/project-group-removal-targets.test.ts": 8, + "src/renderer/src/store/slices/purge-stale-runtime-host-ownership.test.ts": 50, + "src/renderer/src/store/slices/purge-stale-runtime-host-state.test.ts": 137, + "src/renderer/src/store/slices/rate-limits.test.ts": 7, + "src/renderer/src/store/slices/readopted-ssh-worktree-rows.test.ts": 6, + "src/renderer/src/store/slices/recently-closed-tabs.test.ts": 211, + "src/renderer/src/store/slices/remote-server-updates.integration.test.ts": 15, + "src/renderer/src/store/slices/repo-identity-reconcile.test.ts": 12, + "src/renderer/src/store/slices/repo-owner-cache-identity.test.ts": 5, + "src/renderer/src/store/slices/repo-reorder-host-split.test.ts": 5, + "src/renderer/src/store/slices/repos-add-races.test.ts": 56, + "src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts": 76, + "src/renderer/src/store/slices/repos-all-hosts-generation.test.ts": 114, + "src/renderer/src/store/slices/repos-all-hosts.test.ts": 148, + "src/renderer/src/store/slices/repos-catalog-merge-identity.test.ts": 88, + "src/renderer/src/store/slices/repos-cross-host-project-collisions.test.ts": 117, + "src/renderer/src/store/slices/repos-cross-host-refresh-identity.test.ts": 91, + "src/renderer/src/store/slices/repos-ephemeral-vm-cleanup-retention.test.ts": 32, + "src/renderer/src/store/slices/repos-host-identity-routing.test.ts": 191, + "src/renderer/src/store/slices/repos-manual-order-hydration.test.ts": 172, + "src/renderer/src/store/slices/repos-module-lifetime-coordinators.test.ts": 71, + "src/renderer/src/store/slices/repos-nested-import-refresh-failures.test.ts": 57, + "src/renderer/src/store/slices/repos-nested-ssh-projection.test.ts": 52, + "src/renderer/src/store/slices/repos-onboarding-folder-startup.test.ts": 349, + "src/renderer/src/store/slices/repos-paired-runtime-add.test.ts": 46, + "src/renderer/src/store/slices/repos-project-group-create-race.test.ts": 101, + "src/renderer/src/store/slices/repos-project-groups-delete.test.ts": 86, + "src/renderer/src/store/slices/repos-project-groups-owner-routing.test.ts": 73, + "src/renderer/src/store/slices/repos-project-groups.test.ts": 200, + "src/renderer/src/store/slices/repos-project-host-capability.test.ts": 93, + "src/renderer/src/store/slices/repos-project-host-lifecycle.test.ts": 50, + "src/renderer/src/store/slices/repos-project-runtime.test.ts": 77, + "src/renderer/src/store/slices/repos-refresh-identity.test.ts": 128, + "src/renderer/src/store/slices/repos-remove-missing-remote-project.test.ts": 57, + "src/renderer/src/store/slices/repos-remove-project-purge-leak.test.ts": 87, + "src/renderer/src/store/slices/repos-runtime-project-groups.test.ts": 56, + "src/renderer/src/store/slices/repos-runtime-visibility-defaults.test.ts": 43, + "src/renderer/src/store/slices/repos-selected-owner-routing.test.ts": 194, + "src/renderer/src/store/slices/repos-setup-script-dismissals.test.ts": 52, + "src/renderer/src/store/slices/repos-shared-project-badge-color.test.ts": 67, + "src/renderer/src/store/slices/repos-ssh-host-reconciliation.test.ts": 80, + "src/renderer/src/store/slices/repos-stale-fetch.test.ts": 93, + "src/renderer/src/store/slices/repos-update-serialization.test.ts": 52, + "src/renderer/src/store/slices/repos.runtime-fallback.test.ts": 73, + "src/renderer/src/store/slices/repos.test.ts": 185, + "src/renderer/src/store/slices/restored-editor-owner-reparent.test.ts": 106, + "src/renderer/src/store/slices/runtime-catalog-merge-removed-env-guard.test.ts": 45, + "src/renderer/src/store/slices/runtime-environment-ssh.test.ts": 93, + "src/renderer/src/store/slices/runtime-host-purge-session-partition-split.test.ts": 50, + "src/renderer/src/store/slices/runtime-pane-title-sort-epoch.test.ts": 189, + "src/renderer/src/store/slices/runtime-status-catalog-identity.test.ts": 94, + "src/renderer/src/store/slices/runtime-status-first-publication.test.ts": 6, + "src/renderer/src/store/slices/runtime-status-refresh-diagnostics.test.ts": 7, + "src/renderer/src/store/slices/runtime-status-restored-browser-host-attach.test.ts": 9, + "src/renderer/src/store/slices/runtime-status-snapshot.test.ts": 8, + "src/renderer/src/store/slices/runtime-status.test.ts": 187, + "src/renderer/src/store/slices/runtime-switch-settings-persistence.test.ts": 37, + "src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts": 13, + "src/renderer/src/store/slices/set-runtime-environments-purge-wiring.test.ts": 46, + "src/renderer/src/store/slices/settings-owner-hydration-write-fence.test.ts": 211, + "src/renderer/src/store/slices/settings-search-state.test.ts": 8, + "src/renderer/src/store/slices/settings.test.ts": 338, + "src/renderer/src/store/slices/sparse-presets-repo-removal-purge-leak.test.ts": 78, + "src/renderer/src/store/slices/sparse-presets.test.ts": 26, + "src/renderer/src/store/slices/ssh.test.ts": 89, + "src/renderer/src/store/slices/stale-runtime-host-rows.test.ts": 15, + "src/renderer/src/store/slices/store-active-worktree-selection.test.ts": 87, + "src/renderer/src/store/slices/store-active-worktree-split-groups.test.ts": 94, + "src/renderer/src/store/slices/store-active-worktree-tab-close.test.ts": 159, + "src/renderer/src/store/slices/store-active-worktree-terminal-creation.test.ts": 88, + "src/renderer/src/store/slices/store-create-tab-id-hint.test.ts": 57, + "src/renderer/src/store/slices/store-session-browser-hydration.test.ts": 117, + "src/renderer/src/store/slices/store-session-cascades.test.ts": 86, + "src/renderer/src/store/slices/store-session-editor-hydration.test.ts": 111, + "src/renderer/src/store/slices/store-session-terminal-activity.test.ts": 169, + "src/renderer/src/store/slices/store-session-terminal-reconnect.test.ts": 78, + "src/renderer/src/store/slices/store-session-workspace-hydration.test.ts": 84, + "src/renderer/src/store/slices/store-sleep-agent-status-retention.test.ts": 122, + "src/renderer/src/store/slices/store-sleep-exact-runtime-stop.test.ts": 147, + "src/renderer/src/store/slices/store-sleep-pane-hibernation.test.ts": 96, + "src/renderer/src/store/slices/store-sleep-runtime-convergence.test.ts": 448, + "src/renderer/src/store/slices/store-sleep-shutdown-order.test.ts": 49, + "src/renderer/src/store/slices/store-worktree-removal-cascade.test.ts": 141, + "src/renderer/src/store/slices/superseded-ssh-repo-rows.test.ts": 7, + "src/renderer/src/store/slices/tab-group-reference-repair.test.ts": 15, + "src/renderer/src/store/slices/tab-group-state.test.ts": 19, + "src/renderer/src/store/slices/tab-view-mode.test.ts": 86, + "src/renderer/src/store/slices/tab-worktree-orphan-map-purge-leak.test.ts": 27, + "src/renderer/src/store/slices/tabs-empty-split-activation.test.ts": 29, + "src/renderer/src/store/slices/tabs-hydration-generated-title.test.ts": 12, + "src/renderer/src/store/slices/tabs-hydration-group-validation.test.ts": 15, + "src/renderer/src/store/slices/tabs-hydration.test.ts": 12, + "src/renderer/src/store/slices/tabs-label-and-pin-state.test.ts": 68, + "src/renderer/src/store/slices/tabs-model-reconciliation.test.ts": 83, + "src/renderer/src/store/slices/tabs-open-close-lifecycle.test.ts": 185, + "src/renderer/src/store/slices/tabs-pane-layout-operations.test.ts": 88, + "src/renderer/src/store/slices/tabs-session-hydration.test.ts": 67, + "src/renderer/src/store/slices/tabs-unread-and-focus.test.ts": 71, + "src/renderer/src/store/slices/tabs/hydrated-workspace-reconciliation-batch.test.ts": 423, + "src/renderer/src/store/slices/tabs/tab-selection-contract.test.ts": 12, + "src/renderer/src/store/slices/tabs/tabs-reconciliation-batch-identity.test.ts": 49, + "src/renderer/src/store/slices/task-creation-drafts.test.ts": 6, + "src/renderer/src/store/slices/terminal-helpers.test.ts": 11, + "src/renderer/src/store/slices/terminal-input-activity-store-write.test.ts": 51, + "src/renderer/src/store/slices/terminal-layout-pty-ownership.test.ts": 63, + "src/renderer/src/store/slices/terminal-orphan-helpers.test.ts": 8, + "src/renderer/src/store/slices/terminal-pane-detach-agent-identity.test.ts": 33, + "src/renderer/src/store/slices/terminal-pane-detach-agent-retention.test.ts": 92, + "src/renderer/src/store/slices/terminal-pty-identity-replacement.test.ts": 36, + "src/renderer/src/store/slices/terminal-quick-command-hosts.test.ts": 132, + "src/renderer/src/store/slices/terminal-startup-command-retention.test.ts": 80, + "src/renderer/src/store/slices/terminal-tab-id-hydration.test.ts": 38, + "src/renderer/src/store/slices/terminal-tab-owner-index.test.ts": 6, + "src/renderer/src/store/slices/terminal-tab-recovery-remount.test.ts": 151, + "src/renderer/src/store/slices/terminal-tab-retirement-store.test.ts": 172, + "src/renderer/src/store/slices/terminal-tab-retirement.test.ts": 15, + "src/renderer/src/store/slices/terminal-tab-title-batch.test.ts": 147, + "src/renderer/src/store/slices/terminal-write-identity-bailouts.test.ts": 85, + "src/renderer/src/store/slices/terminals-explicit-empty-hydration.test.ts": 61, + "src/renderer/src/store/slices/terminals-hydration-canonical-pty-overlap.test.ts": 87, + "src/renderer/src/store/slices/terminals-hydration-canonical-rows.test.ts": 76, + "src/renderer/src/store/slices/terminals-hydration.test.ts": 145, + "src/renderer/src/store/slices/ui-acknowledge-agents-clock-skew.test.ts": 17, + "src/renderer/src/store/slices/ui-agent-send-target.test.ts": 58, + "src/renderer/src/store/slices/ui-contextual-tours.test.ts": 51, + "src/renderer/src/store/slices/ui-feature-interactions.test.ts": 31, + "src/renderer/src/store/slices/ui-hydration-view-layout.test.ts": 46, + "src/renderer/src/store/slices/ui-hydration-workspace-cleanup-browse.test.ts": 11, + "src/renderer/src/store/slices/ui-hydration-workspace-preferences.test.ts": 45, + "src/renderer/src/store/slices/ui-modal-slot-dismissal.test.ts": 184, + "src/renderer/src/store/slices/ui-new-workspace-draft.test.ts": 8, + "src/renderer/src/store/slices/ui-notice-dismissals.test.ts": 52, + "src/renderer/src/store/slices/ui-page-navigation.test.ts": 51, + "src/renderer/src/store/slices/usage-snapshot-refresh.benchmark.test.ts": 6, + "src/renderer/src/store/slices/usage-web-client-fallback.test.ts": 12, + "src/renderer/src/store/slices/workspace-cleanup-browse.test.ts": 21, + "src/renderer/src/store/slices/workspace-cleanup-cache-hydration.test.ts": 174, + "src/renderer/src/store/slices/workspace-cleanup-enrichment-performance.test.ts": 285, + "src/renderer/src/store/slices/workspace-cleanup-host-qualified-list-state.test.ts": 71, + "src/renderer/src/store/slices/workspace-cleanup-local-evidence-invariants.test.ts": 7, + "src/renderer/src/store/slices/workspace-cleanup-removal-preflight.test.ts": 62, + "src/renderer/src/store/slices/workspace-cleanup-scan-progress-boundary.test.ts": 5, + "src/renderer/src/store/slices/workspace-cleanup-scan-progress.test.ts": 584, + "src/renderer/src/store/slices/workspace-cleanup-unverified-removal-consent.test.ts": 44, + "src/renderer/src/store/slices/workspace-cleanup-wrong-host-removal-guard.test.ts": 99, + "src/renderer/src/store/slices/workspace-cleanup-wrong-host-removal.test.ts": 143, + "src/renderer/src/store/slices/workspace-document-title-refresh.test.ts": 48, + "src/renderer/src/store/slices/workspace-space.test.ts": 11, + "src/renderer/src/store/slices/worktree-by-id-index.test.ts": 6, + "src/renderer/src/store/slices/worktree-catalog-reconciliation.test.ts": 12, + "src/renderer/src/store/slices/worktree-helpers.test.ts": 6, + "src/renderer/src/store/slices/worktree-listing-branch-switch.test.ts": 11, + "src/renderer/src/store/slices/worktree-meta-update-application.test.ts": 8, + "src/renderer/src/store/slices/worktree-nav-history-view-entries.test.ts": 21, + "src/renderer/src/store/slices/worktree-nav-history.test.ts": 12, + "src/renderer/src/store/slices/worktree-removal-maps-leak.test.ts": 91, + "src/renderer/src/store/slices/worktree-sleep-intent-lifecycle.test.ts": 78, + "src/renderer/src/store/slices/worktree-terminal-removal-teardown.test.ts": 79, + "src/renderer/src/store/slices/worktree-visibility-owner-settings.test.ts": 14, + "src/renderer/src/store/slices/worktree-visibility-settings-write.test.ts": 32, + "src/renderer/src/store/slices/worktrees-activity-persistence.test.ts": 66, + "src/renderer/src/store/slices/worktrees-create-base-status.test.ts": 33, + "src/renderer/src/store/slices/worktrees-create-parent-pick.test.ts": 27, + "src/renderer/src/store/slices/worktrees-fetch-listing-merge.test.ts": 42, + "src/renderer/src/store/slices/worktrees-fetch-owner-routing.test.ts": 173, + "src/renderer/src/store/slices/worktrees-fetch-persisted-metadata-fallback.test.ts": 25, + "src/renderer/src/store/slices/worktrees-fetch-refresh-coalescing.test.ts": 40, + "src/renderer/src/store/slices/worktrees-fetch-remote-lineage.test.ts": 25, + "src/renderer/src/store/slices/worktrees-fetch-removal-purge.test.ts": 87, + "src/renderer/src/store/slices/worktrees-git-identity-branch-title.test.ts": 17, + "src/renderer/src/store/slices/worktrees-git-identity-review-clear-persistence.test.ts": 488, + "src/renderer/src/store/slices/worktrees-hydration-purge.test.ts": 59, + "src/renderer/src/store/slices/worktrees-identity-migration.test.ts": 292, + "src/renderer/src/store/slices/worktrees-lineage-state.test.ts": 41, + "src/renderer/src/store/slices/worktrees-linked-review-push-target.test.ts": 69, + "src/renderer/src/store/slices/worktrees-metadata-persistence.test.ts": 43, + "src/renderer/src/store/slices/worktrees-pending-creation-state.test.ts": 70, + "src/renderer/src/store/slices/worktrees-purge-terminal-state.test.ts": 11, + "src/renderer/src/store/slices/worktrees-remote-runtime-create.test.ts": 35, + "src/renderer/src/store/slices/worktrees-remote-runtime-removal.test.ts": 49, + "src/renderer/src/store/slices/worktrees-removal-state-cleanup.test.ts": 60, + "src/renderer/src/store/slices/worktrees-runtime-connection-generation-fence.test.ts": 81, + "src/renderer/src/store/slices/worktrees-runtime-host-metadata-retirement.test.ts": 25, + "src/renderer/src/store/slices/worktrees-terminal-pr-url-linking.test.ts": 15, + "src/renderer/src/store/slices/worktrees-unread-state.test.ts": 19, + "src/renderer/src/store/slices/worktrees-workspace-selection-state.test.ts": 27, + "src/renderer/src/store/slices/worktrees/listing/detected-worktree-meta.test.ts": 8, + "src/renderer/src/store/slices/worktrees/listing/detected-worktree-unavailable-reason.test.ts": 7, + "src/renderer/src/store/slices/worktrees/metadata/worktree-meta-persist.test.ts": 14, + "src/renderer/src/store/slices/worktrees/teardown/host-qualified-removal-refusal.test.ts": 6, + "src/renderer/src/store/slices/worktrees/teardown/record-key-omission.test.ts": 7, + "src/renderer/src/store/slices/worktrees/teardown/remove-worktree-map-identity.test.ts": 9, + "src/renderer/src/store/slices/worktrees/teardown/worktree-teardown-array-identity.test.ts": 9, + "src/renderer/src/store/store-identity-churn-probe.test.ts": 19, + "src/renderer/src/store/store-listener-census.test.ts": 7, + "src/renderer/src/store/terminals/restored-relay-session-identity.test.ts": 88, + "src/renderer/src/store/terminals/structured-chat-tab-rename.test.ts": 68, + "src/renderer/src/store/terminals/terminal-pane-expansion-write-bailout.test.tsx": 113, + "src/renderer/src/store/terminals/terminal-shutdown-guards-identity.test.ts": 5, + "src/renderer/src/store/terminals/terminal-shutdown-map-identity.test.ts": 7, + "src/renderer/src/store/terminals/terminal-tab-close-map-identity.test.ts": 68, + "src/renderer/src/store/terminals/terminal-tab-close-tombstone.test.ts": 86, + "src/renderer/src/store/terminals/terminal-workspace-routing.scale.test.ts": 9, + "src/renderer/src/store/terminals/workspace-terminal-placeholders.test.ts": 10, + "src/renderer/src/store/worktree-diff-comments-selector.test.ts": 53, + "src/renderer/src/store/worktree-host-collision-index.test.ts": 7, + "src/renderer/src/store/worktree-visibility-defaults-by-host.test.ts": 5, + "src/renderer/src/web/preload-api/web-gitlab-api.test.ts": 23, + "src/renderer/src/web/web-clipboard-copy-fallback.test.ts": 12, + "src/renderer/src/web/web-clipboard-copy-terminal-selection.test.ts": 12, + "src/renderer/src/web/web-file-mutation-methods.test.ts": 14, + "src/renderer/src/web/web-pairing.test.ts": 11, + "src/renderer/src/web/web-preload-api-agent-providers.test.ts": 341, + "src/renderer/src/web/web-preload-api-clipboard.test.ts": 1137, + "src/renderer/src/web/web-preload-api-composition.test.ts": 443, + "src/renderer/src/web/web-preload-api-filesystem.test.ts": 413, + "src/renderer/src/web/web-preload-api-git.test.ts": 391, + "src/renderer/src/web/web-preload-api-github.test.ts": 312, + "src/renderer/src/web/web-preload-api-gitlab.test.ts": 596, + "src/renderer/src/web/web-preload-api-keybindings.test.ts": 444, + "src/renderer/src/web/web-preload-api-runtime-calls.test.ts": 755, + "src/renderer/src/web/web-preload-api-runtime-environment.test.ts": 1195, + "src/renderer/src/web/web-preload-api-settings.test.ts": 1562, + "src/renderer/src/web/web-preload-api-ssh.test.ts": 640, + "src/renderer/src/web/web-preload-api-ui.test.ts": 901, + "src/renderer/src/web/web-preload-api-workspace-catalog.test.ts": 903, + "src/renderer/src/web/web-preload-updater-package-recovery.test.ts": 370, + "src/renderer/src/web/web-runtime-client-export-parity.test.ts": 6, + "src/renderer/src/web/web-runtime-client-file-watch-replay.test.ts": 64, + "src/renderer/src/web/web-runtime-client-heartbeat.test.ts": 42, + "src/renderer/src/web/web-runtime-client-timeout-budget.test.ts": 45, + "src/renderer/src/web/web-runtime-client.test.ts": 351, + "src/renderer/src/web/web-runtime-connection-frame-router.test.ts": 6, + "src/renderer/src/web/web-runtime-connection-heartbeat-unsendable-probe.test.ts": 55, + "src/renderer/src/web/web-runtime-status-owner.test.ts": 651, + "src/renderer/src/web/web-viewport-shell.test.ts": 8, + "src/renderer/src/web/web-workspace-session.test.ts": 8, + "src/shared/add-repo-existing-workspaces-telemetry.test.ts": 9, + "src/shared/agent-cli-flag-detection.test.ts": 7, + "src/shared/agent-cli-install-dir-fallback.test.ts": 26, + "src/shared/agent-decorative-title-signature.test.ts": 11, + "src/shared/agent-detection.test.ts": 57, + "src/shared/agent-feature-install-commands.test.ts": 13, + "src/shared/agent-hook-endpoint-file.test.ts": 5, + "src/shared/agent-hook-endpoint-temp-cleanup.test.ts": 14, + "src/shared/agent-hook-listener-antigravity.test.ts": 17, + "src/shared/agent-hook-listener-claude-compatible-vendors.test.ts": 20, + "src/shared/agent-hook-listener-claude-subagents.test.ts": 21, + "src/shared/agent-hook-listener-claude-turn-state.test.ts": 19, + "src/shared/agent-hook-listener-command-code-transcript.test.ts": 114, + "src/shared/agent-hook-listener-extraction-characterization.test.ts": 23, + "src/shared/agent-hook-listener-grok.test.ts": 32, + "src/shared/agent-hook-listener-hermes-codex-droid.test.ts": 13, + "src/shared/agent-hook-listener-interactive-prompts.test.ts": 17, + "src/shared/agent-hook-listener-pi-compatible.test.ts": 15, + "src/shared/agent-hook-listener-relay-dependency.test.ts": 22, + "src/shared/agent-hook-listener-roster-retention.test.ts": 54, + "src/shared/agent-hook-listener-session-replacement.test.ts": 15, + "src/shared/agent-hook-listener-startless-child-lifecycle.test.ts": 22, + "src/shared/agent-hook-listener-transport.test.ts": 49, + "src/shared/agent-hook-listener/transcript-reader.test.ts": 93, + "src/shared/agent-hook-relay.test.ts": 9, + "src/shared/agent-hook-request-body-memory.test.ts": 53, + "src/shared/agent-hook-spool-read.test.ts": 9, + "src/shared/agent-hook-status-cache.test.ts": 5, + "src/shared/agent-hook-transport-interference.test.ts": 9, + "src/shared/agent-kind.test.ts": 9, + "src/shared/agent-launch-remote.test.ts": 7, + "src/shared/agent-model-probe-spec.test.ts": 9, + "src/shared/agent-notification-id.test.ts": 8, + "src/shared/agent-process-recognition.test.ts": 20, + "src/shared/agent-prompt-injection.test.ts": 7, + "src/shared/agent-resume-argv-drop.test.ts": 11, + "src/shared/agent-resume-launch-command.test.ts": 63, + "src/shared/agent-row-conversation-name.test.ts": 10, + "src/shared/agent-scratch-worktrees.test.ts": 7, + "src/shared/agent-session-conversation-name.test.ts": 9, + "src/shared/agent-session-definitive-refusal.test.ts": 11, + "src/shared/agent-session-journal-schemas.test.ts": 35, + "src/shared/agent-session-lease-adjudication.test.ts": 16, + "src/shared/agent-session-mutation-envelope.test.ts": 10, + "src/shared/agent-session-operation-ledger.test.ts": 9, + "src/shared/agent-session-option-catalog-grok.test.ts": 12, + "src/shared/agent-session-option-catalog.test.ts": 10, + "src/shared/agent-session-provider-handle.test.ts": 21, + "src/shared/agent-session-pty-write-admission.test.ts": 8, + "src/shared/agent-session-question-answer.test.ts": 9, + "src/shared/agent-session-resume.test.ts": 9, + "src/shared/agent-session-turn-record.test.ts": 9, + "src/shared/agent-skill-sharing-contract.test.ts": 9, + "src/shared/agent-skill-sharing-gate.test.ts": 6, + "src/shared/agent-status-observation.test.ts": 13, + "src/shared/agent-status-osc-pending-retention.test.ts": 73, + "src/shared/agent-status-osc-scan-budget.test.ts": 195, + "src/shared/agent-status-osc-split-frame-scan-budget.test.ts": 96, + "src/shared/agent-status-osc.test.ts": 16, + "src/shared/agent-status-types.test.ts": 29, + "src/shared/agent-tab-title.test.ts": 14, + "src/shared/agent-terminal-status-equivalence.test.ts": 4, + "src/shared/agent-title-agy-gemini-collision.test.ts": 23, + "src/shared/agent-title-decoration.test.ts": 9, + "src/shared/agent-title-evidence.test.ts": 43, + "src/shared/agent-title-identity-characterization.test.ts": 11, + "src/shared/agent-tui-input-clear.test.ts": 13, + "src/shared/ai-vault-resume-command.test.ts": 7, + "src/shared/ai-vault-resume-preparation.test.ts": 5, + "src/shared/ai-vault-scan-error-message.test.ts": 14, + "src/shared/ai-vault-search-query-operators.test.ts": 10, + "src/shared/ai-vault-session-depth.test.ts": 17, + "src/shared/ai-vault-session-filters.test.ts": 172, + "src/shared/ai-vault-types.test.ts": 9, + "src/shared/app-version.test.ts": 12, + "src/shared/artifact-sharing-gate.test.ts": 9, + "src/shared/automation-execution-target.test.ts": 11, + "src/shared/automation-host-filter.test.ts": 5, + "src/shared/automation-legacy-list-partition.test.ts": 8, + "src/shared/automation-list-response.test.ts": 10, + "src/shared/automation-list-scope.test.ts": 19, + "src/shared/automation-owner-key.test.ts": 10, + "src/shared/automation-owner-precondition.test.ts": 11, + "src/shared/automation-run-identity.test.ts": 5, + "src/shared/automation-run-retention.test.ts": 19, + "src/shared/automation-schedules.test.ts": 183, + "src/shared/automation-usage-summary.test.ts": 9, + "src/shared/base-ref-search-result.test.ts": 6, + "src/shared/binary-file-extensions.test.ts": 4, + "src/shared/bounded-map.test.ts": 17, + "src/shared/branch-name-from-work.test.ts": 11, + "src/shared/branch-prefix.test.ts": 12, + "src/shared/browser-client-automation-protocol.test.ts": 12, + "src/shared/browser-client-host-id-argument.test.ts": 6, + "src/shared/browser-client-host-protocol.test.ts": 241, + "src/shared/browser-client-host-reconciliation-protocol.test.ts": 22, + "src/shared/browser-client-hosting-eligibility.test.ts": 9, + "src/shared/browser-cookie-import-sources.test.ts": 5, + "src/shared/browser-grab-types.test.ts": 11, + "src/shared/browser-network-capabilities.test.ts": 10, + "src/shared/browser-network-tunnel-protocol.test.ts": 11, + "src/shared/browser-network-tunnel-stream-framing.test.ts": 1908, + "src/shared/browser-screencast-protocol.test.ts": 7, + "src/shared/browser-url.test.ts": 14, + "src/shared/cheap-process-table-snapshot.test.ts": 10, + "src/shared/check-job-log-tail-slice.test.ts": 7, + "src/shared/child-process/child-process-import-boundary.test.ts": 9, + "src/shared/child-process/close-process-registry.test.ts": 8, + "src/shared/child-process/process-tree-termination.test.ts": 16, + "src/shared/child-process/retryable-process-exit-proof.test.ts": 8, + "src/shared/child-process/run-process-termination-failure.test.ts": 12, + "src/shared/child-process/run-process.test.ts": 7159, + "src/shared/child-process/windows-cmd-shim-resolution.test.ts": 15, + "src/shared/child-process/windows-command-line.test.ts": 14, + "src/shared/child-process/windows-console-visibility.test.ts": 7, + "src/shared/claimed-agent-pty-owner.test.ts": 33, + "src/shared/claude-agent-teams-tmux-compat.test.ts": 8, + "src/shared/claude-background-task-status.test.ts": 36, + "src/shared/claude-model-list-probe.test.ts": 14, + "src/shared/claude-statusline-rate-limits.test.ts": 7, + "src/shared/claude-subagent-roster.test.ts": 17, + "src/shared/claude-subagent-row-lifecycle.test.ts": 14, + "src/shared/cli-argument-boundary.test.ts": 5, + "src/shared/cli-runtime-pairing-boundary.test.ts": 182, + "src/shared/cli-workspace-provenance.test.ts": 6, + "src/shared/client-environment-info.test.ts": 10, + "src/shared/clipboard-image.test.ts": 9, + "src/shared/clipboard-text.test.ts": 29, + "src/shared/closed-terminal-tab-tombstones.test.ts": 12, + "src/shared/cloud-service-url.test.ts": 4, + "src/shared/codex-auth-errors.test.ts": 22, + "src/shared/codex-pet-sprite-defaults.test.ts": 12, + "src/shared/codex-reset-credit-attempt-ledger.test.ts": 16, + "src/shared/codex-reset-credit-scope.test.ts": 8, + "src/shared/codex-startup-delivery.test.ts": 8, + "src/shared/codex-subagent-poll-scheduler.test.ts": 14, + "src/shared/codex-subagent-rollout-lifecycle.test.ts": 18, + "src/shared/codex-subagent-roster.test.ts": 6, + "src/shared/codex-subagent-transcript.test.ts": 18, + "src/shared/combined-diff-file-tree-width.test.ts": 6, + "src/shared/command-code-output-status.test.ts": 155, + "src/shared/command-code-turn-boundary.test.ts": 6, + "src/shared/command-token-scanner.test.ts": 11, + "src/shared/commit-message-agent-spec.test.ts": 38, + "src/shared/commit-message-generation.test.ts": 12, + "src/shared/commit-message-plan.test.ts": 21, + "src/shared/commit-message-prompt.test.ts": 28, + "src/shared/computer-awake-mode.test.ts": 7, + "src/shared/computer-use-error-recovery.test.ts": 6, + "src/shared/computer-use-key-spec.test.ts": 7, + "src/shared/constants.test.ts": 15, + "src/shared/contextual-tours.test.ts": 16, + "src/shared/crash-reporting.test.ts": 18, + "src/shared/cross-platform-path-guards.test.ts": 1239, + "src/shared/cross-platform-path.test.ts": 17, + "src/shared/daemon-adoption-telemetry.test.ts": 10, + "src/shared/direct-ssh-reconnect-telemetry-schema.test.ts": 17, + "src/shared/doc-preview-file-access.test.ts": 36, + "src/shared/doc-preview-scheme.test.ts": 8, + "src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts": 7, + "src/shared/draft-paste-ready-scanner.test.ts": 19, + "src/shared/emoji-shortcode-catalog.lazy.test.ts": 42, + "src/shared/emulator-keyboard-frame.test.ts": 7, + "src/shared/emulator-touch-frame.test.ts": 5, + "src/shared/ephemeral-setup-terminal-worktree-id.test.ts": 6, + "src/shared/ephemeral-vm-recipe-checkout-mode.test.ts": 7, + "src/shared/ephemeral-vm-recipe-doctor.test.ts": 10, + "src/shared/ephemeral-vm-recipe-process.test.ts": 154, + "src/shared/ephemeral-vm-recipe-repo-url.test.ts": 7, + "src/shared/ephemeral-vm-recipes.test.ts": 23, + "src/shared/ephemeral-vm-runtime-feature-sorting.test.ts": 145, + "src/shared/ephemeral-vm-runtime-store-rollback.test.ts": 341, + "src/shared/ephemeral-vm-runtime-store.test.ts": 41, + "src/shared/event-loop-yield.test.ts": 28, + "src/shared/execution-host-registry.test.ts": 15, + "src/shared/execution-host.test.ts": 14, + "src/shared/export-let-function-initializer-ban.test.ts": 2085, + "src/shared/external-automation-jobs-file.test.ts": 48, + "src/shared/external-worktree-inbox.test.ts": 11, + "src/shared/feature-education-telemetry.test.ts": 5, + "src/shared/feature-interactions.test.ts": 1489, + "src/shared/feature-tips.test.ts": 7, + "src/shared/feature-wall-tour-depth.test.ts": 5, + "src/shared/file-link-location.test.ts": 7, + "src/shared/file-name-sort.test.ts": 6, + "src/shared/filesystem-directory-listing-limit.test.ts": 10, + "src/shared/fish-binary-requirement.test.ts": 14, + "src/shared/folder-workspace-execution-host.test.ts": 17, + "src/shared/folder-workspace-worktree.test.ts": 8, + "src/shared/folder-workspaces.test.ts": 6, + "src/shared/foreground-process-ancestry-parity.test.ts": 286, + "src/shared/foreground-process-selection.test.ts": 11, + "src/shared/foreground-wrapper-agent.test.ts": 7, + "src/shared/generated-code-path.test.ts": 10, + "src/shared/git-branch-cleanup.test.ts": 17, + "src/shared/git-branch-compare-head.test.ts": 6, + "src/shared/git-branch-line-total-soft-deadline.test.ts": 3529, + "src/shared/git-branch-line-total.test.ts": 39, + "src/shared/git-capability-cache.test.ts": 10, + "src/shared/git-check-ignore-stdio.test.ts": 8, + "src/shared/git-clone-failure-message.test.ts": 26, + "src/shared/git-config-snapshot-runner.test.ts": 13, + "src/shared/git-configured-branch-target.test.ts": 11, + "src/shared/git-cquoted-path.test.ts": 3, + "src/shared/git-diff-transport-budget.test.ts": 1152, + "src/shared/git-discard-path-safety.test.ts": 22, + "src/shared/git-exec-mutation.test.ts": 10, + "src/shared/git-fetch-head-capability.test.ts": 6, + "src/shared/git-fetch-head-lock-key-derivation.test.ts": 64, + "src/shared/git-fetch-head-lock.test.ts": 108, + "src/shared/git-fork-sync.test.ts": 21, + "src/shared/git-history-graph.test.ts": 10, + "src/shared/git-history-message-allocation.test.ts": 11, + "src/shared/git-history-ref-display.test.ts": 9, + "src/shared/git-history.test.ts": 18, + "src/shared/git-merge-tree-capability.test.ts": 9, + "src/shared/git-metadata-path.test.ts": 13, + "src/shared/git-push-target-validation.test.ts": 6, + "src/shared/git-remote-error.test.ts": 36, + "src/shared/git-remote-identity.test.ts": 10, + "src/shared/git-remote-url-index.test.ts": 11, + "src/shared/git-rev-list-output.test.ts": 8, + "src/shared/git-status-branch-line-total-cache.test.ts": 12, + "src/shared/git-status-conflict-entries.test.ts": 10, + "src/shared/git-status-limit.test.ts": 6, + "src/shared/git-status-line-stat-inputs.test.ts": 6, + "src/shared/git-status-line-stats-cache.test.ts": 10, + "src/shared/git-status-upstream-ref.test.ts": 6, + "src/shared/git-uncommitted-line-stats.test.ts": 54, + "src/shared/git-upstream-status.test.ts": 16, + "src/shared/git-worktree-operation-lock.test.ts": 16, + "src/shared/github/api-availability.test.ts": 6, + "src/shared/github/project-identity.test.ts": 5, + "src/shared/github/project-ref-input.test.ts": 11, + "src/shared/github/project-roadmap-timeline.test.ts": 11, + "src/shared/github/pull-request-auto-merge-availability.test.ts": 6, + "src/shared/github/pull-request-for-branch-outcome.test.ts": 7, + "src/shared/github/pull-request-merge-methods.test.ts": 8, + "src/shared/github/repository-identity-key.test.ts": 5, + "src/shared/github/work-items-query-bounds.test.ts": 9, + "src/shared/gitlab-job-log-excerpt.test.ts": 36, + "src/shared/gitlab-job-trace-check-details.test.ts": 17, + "src/shared/gitlab-pipeline-checks.test.ts": 6, + "src/shared/gitlab-projects.test.ts": 10, + "src/shared/grok-model-list-probe.test.ts": 14, + "src/shared/grok-session-paths.test.ts": 21, + "src/shared/growing-byte-buffer.test.ts": 89, + "src/shared/handled-wire-discriminant.test.ts": 4, + "src/shared/harness-injected-user-turns.test.ts": 8, + "src/shared/hermes-run-ref-retention.test.ts": 6, + "src/shared/hook-command-source-policy.test.ts": 12, + "src/shared/host-balanced-listing-scaling.test.ts": 13, + "src/shared/host-setting-overrides.test.ts": 10, + "src/shared/hosted-review-creation-providers.test.ts": 4, + "src/shared/hosted-review-github.test.ts": 8, + "src/shared/hosted-review-ready-capabilities.test.ts": 5, + "src/shared/hosted-review-refs.test.ts": 6, + "src/shared/image-data-uri.test.ts": 12, + "src/shared/image-paste-following-text.test.ts": 5, + "src/shared/in-flight-promise-dedupe.test.ts": 19, + "src/shared/issue-link-input.test.ts": 9, + "src/shared/jira-issue-url.test.ts": 8, + "src/shared/json-text-structure-limit.test.ts": 9, + "src/shared/keybindings-conflicts.test.ts": 32, + "src/shared/keybindings-default-bindings.test.ts": 32, + "src/shared/keybindings-digit-index.test.ts": 12, + "src/shared/keybindings-double-tap.test.ts": 14, + "src/shared/keybindings-keyboard-layout.test.ts": 8, + "src/shared/keybindings-parse-cache.test.ts": 43, + "src/shared/keybindings-parsing.test.ts": 12, + "src/shared/keybindings-terminal-context.test.ts": 7, + "src/shared/keybindings-unassigned-actions.test.ts": 12, + "src/shared/linear/inline-media.test.ts": 4, + "src/shared/linear/issue-attribute-filter.test.ts": 9, + "src/shared/linear/issue-view-resume-state.test.ts": 15, + "src/shared/linear/links.test.ts": 7, + "src/shared/linear/workspace-types.test.ts": 7, + "src/shared/linux-proc-port-scan-limits.test.ts": 12, + "src/shared/local-account-runtime.test.ts": 7, + "src/shared/local-build-compatibility.test.ts": 5, + "src/shared/localhost-worktree-labels.test.ts": 8, + "src/shared/loose-ref-count.test.ts": 247, + "src/shared/macos-symbolic-hotkeys.test.ts": 14, + "src/shared/managed-agent-command-token.test.ts": 9, + "src/shared/manual-repo-order.test.ts": 12, + "src/shared/map-settled-with-concurrency.test.ts": 20, + "src/shared/map-with-concurrency.test.ts": 58, + "src/shared/markdown-document-listing-limits.test.ts": 5, + "src/shared/markdown-toc-panel-width.test.ts": 7, + "src/shared/mcp-config.test.ts": 29, + "src/shared/mobile-e2ee-v2-contract.test.ts": 15, + "src/shared/mobile-e2ee-v2-framing.test.ts": 13, + "src/shared/mobile-file-directory-limit.test.ts": 11, + "src/shared/mobile-markdown-document.test.ts": 8, + "src/shared/mobile-pairing-connection-mode.test.ts": 9, + "src/shared/mobile-pairing-custom-address.test.ts": 13, + "src/shared/mobile-push-contract.test.ts": 5, + "src/shared/mobile-relay-close-codes.test.ts": 4, + "src/shared/mobile-relay-mint-failure.test.ts": 5, + "src/shared/mobile-relay-pairing-offer.test.ts": 22, + "src/shared/mobile-relay-phone-protocol.test.ts": 12, + "src/shared/model-id-label.test.ts": 4, + "src/shared/modifier-double-tap-detector.test.ts": 10, + "src/shared/native-chat-agent-profiles.test.ts": 17, + "src/shared/native-chat-agent-support.test.ts": 7, + "src/shared/native-chat-ask-fifo.test.ts": 186, + "src/shared/native-chat-ask.test.ts": 12, + "src/shared/native-chat-command-envelope.test.ts": 9, + "src/shared/native-chat-edit-normalize.test.ts": 35, + "src/shared/native-chat-href-routing.test.ts": 11, + "src/shared/native-chat-image-transcript-markers.test.ts": 19, + "src/shared/native-chat-session-option-commands.test.ts": 15, + "src/shared/native-chat-session-option-defaults.test.ts": 15, + "src/shared/native-chat-session-option-snapshot.test.ts": 15, + "src/shared/native-chat-session-option-state.test.ts": 10, + "src/shared/native-chat-slash-commands.test.ts": 31, + "src/shared/native-chat-stream-unsubscribe.test.ts": 3, + "src/shared/native-chat-streaming.test.ts": 9, + "src/shared/native-chat-subagent-summary.test.ts": 11, + "src/shared/native-chat-task-list.test.ts": 11, + "src/shared/native-chat-tool-activity.test.ts": 11, + "src/shared/native-chat-tool-attribution-allocation.test.ts": 20, + "src/shared/native-chat-tool-icon.test.ts": 19, + "src/shared/native-chat-tool-identity.test.ts": 14, + "src/shared/native-chat-tool-pair-limit.test.ts": 74, + "src/shared/native-chat-tool-summary.test.ts": 13, + "src/shared/native-chat-transcript-retention.test.ts": 5, + "src/shared/native-chat-turn-activity.test.ts": 9, + "src/shared/native-chat-turn-status.test.ts": 13, + "src/shared/native-chat-types.test.ts": 4, + "src/shared/native-chat-unverifiable-turn-status.test.ts": 8, + "src/shared/native-file-drop.test.ts": 28, + "src/shared/nested-repo-telemetry-schema.test.ts": 18, + "src/shared/nested-repo-telemetry.test.ts": 15, + "src/shared/nested-worker-depth.test.ts": 8, + "src/shared/network-proxy.test.ts": 9, + "src/shared/network/manual-address.test.ts": 13, + "src/shared/network/server-share-address.test.ts": 24, + "src/shared/new-workspace-dialog-repo.test.ts": 5, + "src/shared/new-workspace/smart-workspace-url-source-results.test.ts": 10, + "src/shared/new-workspace/work-item-lookup-text.test.ts": 8, + "src/shared/new-workspace/workspace-source.test.ts": 8, + "src/shared/new-workspace/worktree-create-retry-policy.test.ts": 7, + "src/shared/node-bounded-file-reader-sync.test.ts": 4, + "src/shared/node-bounded-file-reader.test.ts": 14, + "src/shared/node-bounded-json-stringify.test.ts": 24, + "src/shared/node-file-content-equality.test.ts": 15, + "src/shared/node-markdown-document-discovery.test.ts": 8, + "src/shared/node-pty-spawn-helper.test.ts": 7, + "src/shared/node-readable-text.test.ts": 14, + "src/shared/node-source-copy-content-equality.test.ts": 16, + "src/shared/nul-delimited-fields.test.ts": 5, + "src/shared/nvm-default-alias.test.ts": 38, + "src/shared/omp-pi-semantic-title-preservation.test.ts": 25, + "src/shared/onboarding-tour-telemetry-events.test.ts": 12, + "src/shared/open-in-applications.test.ts": 8, + "src/shared/opencode-permission-status.test.ts": 18, + "src/shared/opencode-terminal-title.test.ts": 4, + "src/shared/orca-yaml-alias-bounds.test.ts": 27, + "src/shared/orca-yaml-bounds.test.ts": 22, + "src/shared/orchestration-ask-timeout.test.ts": 5, + "src/shared/orchestration-check-output.test.ts": 7, + "src/shared/orchestration-compatibility-evidence.test.ts": 8, + "src/shared/orchestration-dispatch-refusal-contract.test.ts": 10, + "src/shared/orchestration-fleet-attention.test.ts": 6, + "src/shared/orchestration-fleet-evidence-clock.test.ts": 8, + "src/shared/orchestration-fleet-projection.test.ts": 12, + "src/shared/orchestration-rpc-contract.test.ts": 12, + "src/shared/orchestration-task-display.test.ts": 9, + "src/shared/orchestration-task-summary.test.ts": 9, + "src/shared/orchestration-timing-budgets.test.ts": 6, + "src/shared/osc-title-scan-tail-retention.test.ts": 34, + "src/shared/osc-title-scan-tail.test.ts": 4, + "src/shared/osc52-clipboard-settings.test.ts": 7, + "src/shared/own-retained-string.test.ts": 77, + "src/shared/pairing-address-auto-selection.test.ts": 5, + "src/shared/pairing-local-ui-fields.test.ts": 6, + "src/shared/pairing.test.ts": 14, + "src/shared/pane-agent-identity-adapter.test.ts": 21, + "src/shared/pane-agent-identity-inventory.test.ts": 782, + "src/shared/pane-agent-identity-resolver.test.ts": 15, + "src/shared/pane-agent-identity-surface-inventory.test.ts": 1226, + "src/shared/pane-agent-identity-title-corpus.test.ts": 17, + "src/shared/pane-agent-owner.test.ts": 8, + "src/shared/pane-key-alias.test.ts": 6, + "src/shared/physical-exit-tracker.test.ts": 11, + "src/shared/pi-agent-kind.test.ts": 4, + "src/shared/pi-overlay-ui-settings.test.ts": 7, + "src/shared/pi-state-title-marker.test.ts": 11, + "src/shared/plugins/plugin-consent-fingerprint.test.ts": 14, + "src/shared/plugins/plugin-consent-request.test.ts": 10, + "src/shared/plugins/plugin-content-pack-contributions.test.ts": 16, + "src/shared/plugins/plugin-demo-fixture.test.ts": 16, + "src/shared/plugins/plugin-hostile-fixture.test.ts": 4, + "src/shared/plugins/plugin-install-lockfile.test.ts": 10, + "src/shared/plugins/plugin-kill-list.test.ts": 47, + "src/shared/plugins/plugin-language-pack-artifact.test.ts": 134, + "src/shared/plugins/plugin-manifest.test.ts": 24, + "src/shared/plugins/plugin-marketplace.test.ts": 64, + "src/shared/plugins/plugin-panel-call-admission.test.ts": 6, + "src/shared/plugins/plugin-panel-message-budget.test.ts": 6, + "src/shared/plugins/plugin-panel-pong-frame.test.ts": 9, + "src/shared/plugins/plugin-panel-shell.test.ts": 7, + "src/shared/plugins/plugin-path-safety.test.ts": 15, + "src/shared/plugins/plugin-vm-recipe-artifact.test.ts": 18, + "src/shared/posix-wait-status.test.ts": 7, + "src/shared/powershell-native-argument.test.ts": 4, + "src/shared/pr-bot-author-overrides.test.ts": 11, + "src/shared/pr-check-severity-order.test.ts": 7, + "src/shared/pr-check-status.test.ts": 5, + "src/shared/pr-comment-audience.test.ts": 5, + "src/shared/pr-comment-groups.test.ts": 8, + "src/shared/pr-comment-time.test.ts": 7, + "src/shared/preferred-git-remote.test.ts": 6, + "src/shared/priority-semaphore.test.ts": 19, + "src/shared/process-output-field-scanner.test.ts": 6, + "src/shared/process-table-snapshot.test.ts": 72, + "src/shared/project-catalog-row-normalization.test.ts": 9, + "src/shared/project-execution-runtime.test.ts": 15, + "src/shared/project-groups.test.ts": 124, + "src/shared/project-host-setup-projection.test.ts": 30, + "src/shared/project-identity-succession.test.ts": 30, + "src/shared/promise-settlement-waiters.test.ts": 337, + "src/shared/protocol-compat.test.ts": 13, + "src/shared/pty-consumer-session.test.ts": 17, + "src/shared/pty-delivery-diagnostics.test.ts": 14, + "src/shared/pty-liveness-verdict.test.ts": 6, + "src/shared/pty-owner-backend.test.ts": 4, + "src/shared/pty-slave-line-discipline-echo.test.ts": 13, + "src/shared/pty-startup-ingress-live-query-reply.test.ts": 23, + "src/shared/pty-startup-ingress.test.ts": 60, + "src/shared/pty-startup-reply-echo-shapes.test.ts": 14, + "src/shared/published-pane-agent-identity.test.ts": 20, + "src/shared/pull-request-generation.test.ts": 11, + "src/shared/quick-open-directory-reader.test.ts": 11, + "src/shared/quick-open-expansion-paths.test.ts": 30, + "src/shared/quick-open-filter.renderer-safety.test.ts": 6, + "src/shared/quick-open-filter.test.ts": 15, + "src/shared/quick-open-git-directory-collapse.test.ts": 51, + "src/shared/quick-open-listing-limits.test.ts": 14, + "src/shared/quick-open-readdir-memory.test.ts": 38, + "src/shared/quick-open-readdir-walk.test.ts": 197, + "src/shared/quick-open-transport-budget.test.ts": 5, + "src/shared/raster-image-base64-preview.test.ts": 322, + "src/shared/raster-image-dimensions.test.ts": 17, + "src/shared/raster-image-preview-limits.test.ts": 7, + "src/shared/rate-limit-reset-format.test.ts": 9, + "src/shared/rate-limit-types.test.ts": 6, + "src/shared/react-update-depth-attribution.test.ts": 7, + "src/shared/reconnect-jitter.test.ts": 5, + "src/shared/relay-frame-buffer.test.ts": 393, + "src/shared/relay-optional-artifacts.test.ts": 5, + "src/shared/relay-version-marker.test.ts": 5, + "src/shared/release-channel.test.ts": 26, + "src/shared/remote-foreground-evidence.test.ts": 9, + "src/shared/remote-pairing-address.test.ts": 18, + "src/shared/remote-pairing-verification.test.ts": 7, + "src/shared/remote-rpc-content-budget.test.ts": 89, + "src/shared/remote-runtime-client-error-classification.test.ts": 8, + "src/shared/remote-runtime-client.test.ts": 4792, + "src/shared/remote-runtime-memory-limits.test.ts": 110, + "src/shared/remote-runtime-outbound-admission.test.ts": 903, + "src/shared/remote-runtime-request-connection-stale.test.ts": 141, + "src/shared/remote-runtime-request-connection.test.ts": 100, + "src/shared/remote-runtime-request-frames.test.ts": 9, + "src/shared/remote-runtime-request-response-router.test.ts": 12, + "src/shared/remote-runtime-request-websocket.test.ts": 32, + "src/shared/remote-runtime-shared-control-boundary.test.ts": 2634, + "src/shared/remote-runtime-shared-control-connection.test.ts": 3301, + "src/shared/remote-runtime-shared-control-keepalive-refresh.test.ts": 14, + "src/shared/remote-runtime-shared-control-reconnect.test.ts": 9, + "src/shared/remote-runtime-shared-control-retired-request-ids.test.ts": 8, + "src/shared/remote-runtime-shared-control-socket-generation.test.ts": 4, + "src/shared/remote-runtime-shared-control-standing-intent.test.ts": 615, + "src/shared/remote-runtime-shared-control-subscription-close.test.ts": 5, + "src/shared/remote-runtime-shared-control-subscriptions.test.ts": 12, + "src/shared/remote-runtime-socket-liveness.test.ts": 9, + "src/shared/remote-runtime-subscription-frame-router.test.ts": 7, + "src/shared/remote-runtime-subscription-request.test.ts": 295, + "src/shared/remote-runtime-tailscale-hint.test.ts": 7, + "src/shared/remote-runtime-transport-error-agreement.test.ts": 9, + "src/shared/remote-workspace-session-projection.test.ts": 9, + "src/shared/renderer-restart-preparation.test.ts": 10, + "src/shared/repo-badge-color.test.ts": 5, + "src/shared/repo-icon.test.ts": 23, + "src/shared/repo-ref-maintenance.test.ts": 316, + "src/shared/repo-search-limits.test.ts": 10, + "src/shared/repro-13889-claude-quarter-circle-busy-title.test.ts": 16, + "src/shared/repro-7732-gitlab-job-id-dropped.test.ts": 5, + "src/shared/repro-8478-opencode-native-title-icon.test.ts": 16, + "src/shared/require-tui-agent-config.test.ts": 4, + "src/shared/resolved-worktree-lineage.test.ts": 10, + "src/shared/retired-pty-incarnations.test.ts": 4, + "src/shared/review-head-tracking-ref.test.ts": 6, + "src/shared/ripgrep-process-availability.test.ts": 11, + "src/shared/runtime-client-export-parity.test.ts": 5, + "src/shared/runtime-environment-store.test.ts": 32, + "src/shared/runtime-host-status-owner.test.ts": 22, + "src/shared/runtime-listing-host-scope.test.ts": 10, + "src/shared/runtime-navigation.test.ts": 5, + "src/shared/runtime-rpc-call-queue.test.ts": 15, + "src/shared/runtime-workspace-file-owner.test.ts": 10, + "src/shared/runtime-workspace-window-availability.test.ts": 5, + "src/shared/search-match-count.test.ts": 7, + "src/shared/search-subprocess-lines.test.ts": 87, + "src/shared/secret-store.test.ts": 8, + "src/shared/secure-file-coarse-ctime.test.ts": 9, + "src/shared/secure-file-fsync-flags.test.ts": 13, + "src/shared/secure-file.test.ts": 602, + "src/shared/secure-path-hardening-cache.test.ts": 9, + "src/shared/secure-path-hardening-retry-budget.test.ts": 39, + "src/shared/serve-option-validation.test.ts": 12, + "src/shared/setup-agent-sequencing.test.ts": 6203, + "src/shared/setup-runner-command.test.ts": 16, + "src/shared/setup-script-imports.test.ts": 26, + "src/shared/setup-script-package-manager-suggestion.test.ts": 14, + "src/shared/setup-script-shebang.test.ts": 7, + "src/shared/setup-script-telemetry-events.test.ts": 10, + "src/shared/setup-script-telemetry.test.ts": 7, + "src/shared/shell-foreground-snapshot.test.ts": 118, + "src/shared/shell-process-readiness.test.ts": 20, + "src/shared/skill-bundle-install-contract.test.ts": 9, + "src/shared/skill-bundle-name.test.ts": 3, + "src/shared/skill-delete-contract.test.ts": 30, + "src/shared/skill-deletion-eligibility.test.ts": 6, + "src/shared/skill-install-contract.test.ts": 13, + "src/shared/skill-install-failure.test.ts": 11, + "src/shared/skill-metadata.test.ts": 6, + "src/shared/skill-package-manifest.test.ts": 78, + "src/shared/skill-path-containment.test.ts": 8, + "src/shared/skills-cli-agent-keys.test.ts": 10, + "src/shared/source-control-ai-action-recipes.test.ts": 11, + "src/shared/source-control-ai-action-variables.test.ts": 12, + "src/shared/source-control-ai-actions.test.ts": 11, + "src/shared/source-control-ai-policy-regression.test.ts": 12, + "src/shared/source-control-ai-recipe-save.test.ts": 15, + "src/shared/source-control-ai.test.ts": 18, + "src/shared/source-control-create-review-intent.test.ts": 12, + "src/shared/source-control-group-order.test.ts": 4, + "src/shared/source-control-primary-action-decision.test.ts": 5, + "src/shared/source-control-push-failure.test.ts": 12, + "src/shared/source-control-recovery-agent-command.test.ts": 7, + "src/shared/source-scan/source-tree-scan.test.ts": 37, + "src/shared/ssh-pending-pty-kill.test.ts": 9, + "src/shared/ssh-pty-id.test.ts": 8, + "src/shared/ssh-relay-pty-ownership-proof.test.ts": 44, + "src/shared/ssh-retained-payload-admission.test.ts": 20, + "src/shared/ssh-target-generation.test.ts": 11, + "src/shared/ssh-types.test.ts": 7, + "src/shared/stable-pane-id.test.ts": 13, + "src/shared/startup-command-submission.test.ts": 6, + "src/shared/status-bar-usage-mode.test.ts": 3, + "src/shared/string-chunk-compaction.test.ts": 19, + "src/shared/structural-value-equality.test.ts": 10, + "src/shared/structured-agent-session-coalescer.test.ts": 7, + "src/shared/structured-agent-session-composer.test.ts": 13, + "src/shared/structured-agent-session-create.test.ts": 16, + "src/shared/structured-agent-session-item-retention.test.ts": 460, + "src/shared/structured-agent-session-live-turn.test.ts": 4, + "src/shared/structured-agent-session-mutation.test.ts": 6, + "src/shared/structured-agent-session-option-picks.test.ts": 8, + "src/shared/structured-agent-session-options.test.ts": 12, + "src/shared/structured-agent-session-projection.test.ts": 14, + "src/shared/structured-agent-session-reducer.test.ts": 43, + "src/shared/structured-agent-session-turn-timing-retention.test.ts": 20, + "src/shared/structured-agent-session-turn-timing.test.ts": 15, + "src/shared/structured-native-chat-launch-route.test.ts": 8, + "src/shared/subprocess-stdin-write.test.ts": 4, + "src/shared/synthetic-agent-title.test.ts": 9, + "src/shared/tab-title-resolution.test.ts": 8, + "src/shared/tailnet-address.test.ts": 5, + "src/shared/task-providers.test.ts": 12, + "src/shared/task-query.test.ts": 24, + "src/shared/task-source-context.test.ts": 12, + "src/shared/telemetry-common-props.test.ts": 9, + "src/shared/telemetry-events-feature-education.test.ts": 12, + "src/shared/telemetry-events.test.ts": 37, + "src/shared/telemetry-feature-wall-events.test.ts": 11, + "src/shared/telemetry-orca-cli-feature-tip.test.ts": 6, + "src/shared/terminal-bell-detector.test.ts": 7, + "src/shared/terminal-color-scheme-protocol.test.ts": 7, + "src/shared/terminal-composer-draft.test.ts": 13, + "src/shared/terminal-custom-themes.test.ts": 10, + "src/shared/terminal-escape-introducer.test.ts": 16, + "src/shared/terminal-exit-cause.test.ts": 8, + "src/shared/terminal-file-url-target.test.ts": 3, + "src/shared/terminal-fonts.test.ts": 7, + "src/shared/terminal-github-pr-link-detector.test.ts": 14, + "src/shared/terminal-input.test.ts": 40, + "src/shared/terminal-kitty-keyboard-mode-tracker.test.ts": 19, + "src/shared/terminal-line-height-settings.test.ts": 4, + "src/shared/terminal-mode-2031-final-state.test.ts": 10, + "src/shared/terminal-mode-reset-profiles.test.ts": 13, + "src/shared/terminal-osc-color-reply.test.ts": 7, + "src/shared/terminal-output-side-effects.test.ts": 14, + "src/shared/terminal-partial-escape-tail-ground-scan.test.ts": 12, + "src/shared/terminal-partial-escape-tail.fuzz.test.ts": 986, + "src/shared/terminal-partial-escape-tail.test.ts": 13, + "src/shared/terminal-query-reply.test.ts": 23, + "src/shared/terminal-quick-commands.test.ts": 10, + "src/shared/terminal-reply-query-scan.test.ts": 5, + "src/shared/terminal-scrollback-policy.test.ts": 11, + "src/shared/terminal-startup-cwd.test.ts": 11, + "src/shared/terminal-stream-protocol.test.ts": 10, + "src/shared/terminal-title-agent-type.test.ts": 27, + "src/shared/terminal-title-classification-corpus.test.ts": 34, + "src/shared/terminal-title-classification-memo.test.ts": 20, + "src/shared/terminal-view-attributes.test.ts": 15, + "src/shared/terminal-wait-blocked-reason-legacy-alias.test.ts": 9, + "src/shared/terminal-zero-dimensions-diagnostic.test.ts": 5, + "src/shared/test-code-path.test.ts": 10, + "src/shared/text-search.test.ts": 53, + "src/shared/timer-delay.test.ts": 9, + "src/shared/tui-agent-config.test.ts": 9, + "src/shared/tui-agent-permissions.test.ts": 10, + "src/shared/tui-agent-selection.test.ts": 10, + "src/shared/tui-agent-startup-copilot-resume.test.ts": 5, + "src/shared/tui-agent-startup-hermes.test.ts": 26, + "src/shared/tui-agent-startup-session-options.test.ts": 13, + "src/shared/tui-agent-startup-shell.test.ts": 23, + "src/shared/tui-agent-startup.test.ts": 29, + "src/shared/ui-language.test.ts": 8, + "src/shared/ui-locale.test.ts": 14, + "src/shared/ui-zoom-level.test.ts": 7, + "src/shared/updated-at-order.test.ts": 82, + "src/shared/updater-windows-signature-check.test.ts": 6, + "src/shared/usage-percentage-display-change-notice.test.ts": 6, + "src/shared/usage-percentage-display.test.ts": 8, + "src/shared/utf8-byte-limits.test.ts": 70, + "src/shared/vscode-remote-ssh-launcher.test.ts": 7, + "src/shared/window-shortcut-policy-agent-dashboard.test.ts": 7, + "src/shared/window-shortcut-policy.test.ts": 25, + "src/shared/windows-cmd-runner-delayed-launch.test.ts": 5, + "src/shared/windows-command-line-budget.test.ts": 1102, + "src/shared/windows-console-input.test.ts": 12, + "src/shared/windows-environment-expansion.test.ts": 48, + "src/shared/windows-interactive-login-spawn.test.ts": 13, + "src/shared/windows-lane-tree-removal-boundary.test.ts": 19, + "src/shared/windows-long-path-git-args.test.ts": 4, + "src/shared/windows-security-descriptor.test.ts": 13, + "src/shared/windows-terminal-shell.test.ts": 9, + "src/shared/windows-transient-lock-removal.test.ts": 1176, + "src/shared/worker-terminal-host-scope.test.ts": 15, + "src/shared/worker-transcript-text-scaling.test.ts": 7, + "src/shared/workspace-cleanup-applied-filters.test.ts": 15, + "src/shared/workspace-cleanup-browse-state-persistence.test.ts": 18, + "src/shared/workspace-cleanup-ui-state.test.ts": 7, + "src/shared/workspace-cleanup.test.ts": 10, + "src/shared/workspace-doc-history.test.ts": 34, + "src/shared/workspace-linked-item-equality.test.ts": 7, + "src/shared/workspace-linked-item-source-context.test.ts": 8, + "src/shared/workspace-name.test.ts": 25, + "src/shared/workspace-session-browser-history.test.ts": 25, + "src/shared/workspace-session-partition-owner.test.ts": 4, + "src/shared/workspace-session-salvage-equivalence.test.ts": 95, + "src/shared/workspace-session-salvage.test.ts": 1522, + "src/shared/workspace-session-schema-field-coverage.test.ts": 15, + "src/shared/workspace-session-schema.sleeping-agent.test.ts": 22, + "src/shared/workspace-session-schema.test.ts": 23, + "src/shared/workspace-session-tab-focus-schema.test.ts": 19, + "src/shared/workspace-session-terminal-buffers.test.ts": 28, + "src/shared/workspace-session-terminal-schema.test.ts": 12, + "src/shared/workspace-session-terminal-tab-close.test.ts": 43, + "src/shared/workspace-session-validation-work.test.ts": 108, + "src/shared/workspace-space-compaction.test.ts": 15, + "src/shared/workspace-space-entry-traversal.test.ts": 1132, + "src/shared/workspace-space-scan-budget.test.ts": 32, + "src/shared/workspace-statuses.test.ts": 24, + "src/shared/worktree-execution-host-resolution.test.ts": 15, + "src/shared/worktree-name-suggestion.test.ts": 29, + "src/shared/worktree/base-ref.test.ts": 11, + "src/shared/worktree/card-properties.test.ts": 6, + "src/shared/worktree/create-preparation.test.ts": 6, + "src/shared/worktree/github-pr-suppression.test.ts": 8, + "src/shared/worktree/host-context-labels.test.ts": 8, + "src/shared/worktree/host-qualified-identity.test.ts": 8, + "src/shared/worktree/id.test.ts": 13, + "src/shared/worktree/identity.test.ts": 7, + "src/shared/worktree/ownership-configured-base-visibility.test.ts": 22, + "src/shared/worktree/ownership-worktree-base-path.test.ts": 8, + "src/shared/worktree/ownership.test.ts": 284, + "src/shared/worktree/removal-fence-error.test.ts": 7, + "src/shared/worktree/removal-force-classification.test.ts": 5, + "src/shared/worktree/retired-name-cache.test.ts": 15, + "src/shared/worktree/retired-name-registry.test.ts": 185, + "src/shared/worktree/sort-order-update.test.ts": 7, + "src/shared/worktree/submodule-removal.test.ts": 7, + "src/shared/worktree/visibility-sources.test.ts": 18, + "src/shared/ws-outbound-backpressure-queue.test.ts": 31, + "src/shared/wsl-exec-mode-separator.test.ts": 2451, + "src/shared/wsl-login-shell-command.test.ts": 66, + "src/shared/wsl-paths.test.ts": 18, + "src/shared/zod-salvage-absence.test.ts": 21, + "tests/e2e/alternate-screen-fixture-script.unit.test.ts": 8, + "tests/e2e/completed-worker-retirement-resume.unit.test.ts": 128, + "tests/e2e/global-teardown.unit.test.ts": 55, + "tests/e2e/helpers/alt-screen-frame.unit.test.ts": 40, + "tests/e2e/helpers/client-hosted-browser-fixture.unit.test.ts": 13, + "tests/e2e/helpers/electron-crashpad-cleanup.unit.test.ts": 11, + "tests/e2e/helpers/electron-home-isolation.unit.test.ts": 7, + "tests/e2e/helpers/electron-launch-args.unit.test.ts": 9, + "tests/e2e/helpers/electron-main-evaluate-retry.unit.test.ts": 1209, + "tests/e2e/helpers/electron-process-shutdown.unit.test.ts": 11, + "tests/e2e/helpers/fake-agent-command-override.unit.test.ts": 3, + "tests/e2e/helpers/fake-agent-paste-end-scanner.unit.test.ts": 16, + "tests/e2e/helpers/git-status-retry-barrier.unit.test.ts": 7, + "tests/e2e/helpers/golden-source-control.unit.test.ts": 11, + "tests/e2e/helpers/headless-paired-runtime-serve-readiness.unit.test.ts": 18, + "tests/e2e/helpers/nested-runtime-proxy-jump-fixture.unit.test.ts": 5, + "tests/e2e/helpers/paired-client-runtime-environment.unit.test.ts": 12, + "tests/e2e/helpers/paired-client-window-reveal.unit.test.ts": 10, + "tests/e2e/helpers/remote-skill-cloud-fixture.unit.test.ts": 138, + "tests/e2e/helpers/remote-terminal-source-range-contract-fixture.unit.test.ts": 7, + "tests/e2e/helpers/ssh-port-forward-snapshot-barrier.unit.test.ts": 57, + "tests/e2e/helpers/streaming-terminal-cleanup.unit.test.ts": 10, + "tests/e2e/host-cold-park-remote-subscriber.unit.test.ts": 518, + "tests/e2e/host-guest-paint-retention-remote-viewer.unit.test.ts": 278, + "tests/e2e/orca-restart-navigation.unit.test.ts": 9, + "tests/e2e/orchestration-run-pagination.unit.test.ts": 9, + "tests/e2e/paired-runtime-rejected-input-remount.unit.test.ts": 935, + "tests/e2e/relay-region-compatibility.unit.test.ts": 56, + "tests/e2e/relay-region-correction.unit.test.ts": 5935, + "tests/e2e/remote-agent-completion-authority.unit.test.ts": 20, + "tests/e2e/remote-terminal-tab-retirement.unit.test.ts": 53, + "tests/e2e/restored-terminal-input-readiness.unit.test.ts": 17, + "tests/e2e/session-tabs-decorative-title-fanout.unit.test.ts": 460, + "tests/e2e/session-tabs-empty-inventory-daemon-oracle.unit.test.ts": 33, + "tests/e2e/session-tabs-rich-status-boundaries.unit.test.ts": 82, + "tests/e2e/ssh-codex-replay-reply-probe.unit.test.ts": 9, + "tests/e2e/structured-native-chat-routing-authority.unit.test.ts": 67, + "tests/e2e/terminal-foreground-confirmation.unit.test.ts": 19, + "tests/e2e/terminal-probe-input-sequence.unit.test.ts": 9, + "tests/e2e/terminal-split-activation-latency-artifact.unit.test.ts": 11, + "tests/e2e/terminal-split-activation-latency-report.unit.test.ts": 13, + "tests/tools/relay-bench/phone-e2ee-desktop-parity.test.mjs": 48, + "tests/tools/relay-bench/region-probe-replay.test.mjs": 26, + "tests/tools/relay-bench/relay-bench-invocation.test.mjs": 16, + "tests/tools/relay-bench/relay-bench-state-file.test.mjs": 13, + "tests/tools/relay-bench/relay-phone-connect-bench.test.mjs": 27, + "tests/tools/win-update-e2e/app-driver.test.mjs": 23, + "tests/tools/windows-pty-native-capability-smoke/packaged-node-pty-capability-oracle.test.mjs": 8, + "tests/tools/windows-pty-native-capability-smoke/packaged-node-pty-capability-probe.test.mjs": 40, + "tests/tools/windows-pty-native-capability-smoke/run.test.mjs": 11 + } + }, + "e2e": { + "runId": "34652504501", + "jobIds": [ + "103437895060", + "103437895072", + "103437895076", + "103437895077", + "103437895093", + "103437895101", + "103437895106", + "103437895107", + "103437895124", + "103437895125", + "103437895133", + "103437895146", + "103437895183", + "103437895216" + ], + "overheadMs": 0, + "timings": { + "tests/e2e/active-view-restart-restore.spec.ts": 22600, + "tests/e2e/activity-agent-pane-isolation.spec.ts": 102400, + "tests/e2e/add-project-default-checkout.spec.ts": 41600, + "tests/e2e/agent-dashboard-status-burst.spec.ts": 16900, + "tests/e2e/agent-descendant-process-kill.spec.ts": 14800, + "tests/e2e/agent-session-live-force-exit-resume.spec.ts": 31800, + "tests/e2e/agent-session-log-tail-stability.spec.ts": 78000, + "tests/e2e/agent-session-quit-resume.spec.ts": 27800, + "tests/e2e/ai-vault-session-delete.spec.ts": 29800, + "tests/e2e/app-menu-paste-ownership.spec.ts": 31900, + "tests/e2e/artificial-opencode-terminal-load.spec.ts": 230300, + "tests/e2e/automation-hidden-terminal-first-mount.spec.ts": 15100, + "tests/e2e/automation-prompt-disclosure.spec.ts": 27000, + "tests/e2e/automation-runs-dashboard.spec.ts": 18900, + "tests/e2e/browser-address-bar-narrow-toolbar.spec.ts": 17100, + "tests/e2e/browser-embedded-owner-routing.spec.ts": 20900, + "tests/e2e/browser-fedcm-fallback.spec.ts": 15100, + "tests/e2e/browser-guest-attachment-validation.spec.ts": 16900, + "tests/e2e/browser-guest-crash-recovery.spec.ts": 123200, + "tests/e2e/browser-loading-surface.spec.ts": 49800, + "tests/e2e/browser-local-https-certificate-trust.spec.ts": 19500, + "tests/e2e/browser-reload-feedback.spec.ts": 21000, + "tests/e2e/browser-split-shortcuts.spec.ts": 75000, + "tests/e2e/browser-tab.spec.ts": 175700, + "tests/e2e/chinese-ime-chat-input-repro.spec.ts": 49200, + "tests/e2e/combined-diff-invalidation-freeze-repro.spec.ts": 101900, + "tests/e2e/combined-diff-scroll-restore.spec.ts": 51900, + "tests/e2e/completed-worker-retirement-resume.spec.ts": 40400, + "tests/e2e/daemon-generation-legacy-close-safety.spec.ts": 5300, + "tests/e2e/daemon-lifecycle-retirement.spec.ts": 222, + "tests/e2e/daemon-live-session-preservation.spec.ts": 20700, + "tests/e2e/daemon-slow-health-check-preservation.spec.ts": 23700, + "tests/e2e/daemon-slow-init-pty-gate.spec.ts": 20700, + "tests/e2e/default-branch-visibility.spec.ts": 14800, + "tests/e2e/dictation-indicator.spec.ts": 14900, + "tests/e2e/diff-note-delete.spec.ts": 18700, + "tests/e2e/diff-note-edit.spec.ts": 20900, + "tests/e2e/diff-note-layout.spec.ts": 24900, + "tests/e2e/droid-notification.spec.ts": 65000, + "tests/e2e/editable-context-paste-ownership.spec.ts": 15700, + "tests/e2e/editor-tab-selection-restore.spec.ts": 28900, + "tests/e2e/electron-home-isolation.spec.ts": 12800, + "tests/e2e/ephemeral-vm-cleanup-retry.spec.ts": 26800, + "tests/e2e/ephemeral-vm-provisioned-root.spec.ts": 60000, + "tests/e2e/feature-wall.spec.ts": 101800, + "tests/e2e/file-explorer-watch-refresh.spec.ts": 17700, + "tests/e2e/file-open.spec.ts": 60000, + "tests/e2e/finished-agent-ghost-resume.spec.ts": 19100, + "tests/e2e/floating-mobile-emulator-tab.spec.ts": 23000, + "tests/e2e/floating-tab-rename.spec.ts": 56200, + "tests/e2e/folder-setup-shallow-priority.spec.ts": 50100, + "tests/e2e/folder-setup.spec.ts": 56200, + "tests/e2e/git-history-tooltip-wrap.spec.ts": 22900, + "tests/e2e/git-no-upstream-polling-churn.spec.ts": 22600, + "tests/e2e/github-cli-stall-repro.spec.ts": 18200, + "tests/e2e/github-created-issue-start-prefill.spec.ts": 43100, + "tests/e2e/github-url-smart-input-transition.spec.ts": 58200, + "tests/e2e/golden-agent-tui-launch.spec.ts": 27300, + "tests/e2e/golden-core-flows.spec.ts": 91100, + "tests/e2e/golden-file-open-edit-save.spec.ts": 25000, + "tests/e2e/golden-fresh-profile-terminal.spec.ts": 24400, + "tests/e2e/golden-posix-fresh-startup.spec.ts": 4900, + "tests/e2e/golden-posix-profile-index-fsync.spec.ts": 4900, + "tests/e2e/golden-quit-relaunch-session.spec.ts": 29700, + "tests/e2e/golden-shell-after-agent-exit.spec.ts": 28300, + "tests/e2e/golden-shell-command.spec.ts": 15400, + "tests/e2e/golden-source-control-commit.spec.ts": 19100, + "tests/e2e/golden-source-control-open-diff.spec.ts": 20900, + "tests/e2e/golden-tab-bar-agent-launch.spec.ts": 54100, + "tests/e2e/golden-terminal-file-link.spec.ts": 43300, + "tests/e2e/golden-worktree-create-switch.spec.ts": 29100, + "tests/e2e/grok-hook-session-cleanup.spec.ts": 7700, + "tests/e2e/headless-paired-remote-terminal-retention-memory.spec.ts": 58000, + "tests/e2e/headless-serve-cli-terminal-retention-parity.spec.ts": 25300, + "tests/e2e/headless-serve-desktop-activation.spec.ts": 16700, + "tests/e2e/headless-serve-focused-terminal-create.spec.ts": 13200, + "tests/e2e/host-parked-pane-remote-viewer.spec.ts": 144000, + "tests/e2e/issue-12656-terminal-link-tooltip.spec.ts": 21000, + "tests/e2e/korean-ime-terminal-shift-enter-commit.spec.ts": 104400, + "tests/e2e/landing-preflight-runtime-routing.spec.ts": 11000, + "tests/e2e/large-diff-freeze-repro.spec.ts": 35800, + "tests/e2e/linear-filter-chip-labels.spec.ts": 29800, + "tests/e2e/linear-issue-view-persistence.spec.ts": 198000, + "tests/e2e/linear-url-workspace-entry.spec.ts": 73500, + "tests/e2e/live-background-terminal-mount-authority.spec.ts": 33300, + "tests/e2e/local-cli-terminal-retention.spec.ts": 18300, + "tests/e2e/local-worktree-visibility-runtime-active.spec.ts": 14700, + "tests/e2e/macos-press-and-hold-startup.spec.ts": 12700, + "tests/e2e/manual-worktree-order-persistence.spec.ts": 36100, + "tests/e2e/markdown-add-review-note-shortcut.spec.ts": 68300, + "tests/e2e/markdown-explorer-find-focus.spec.ts": 16300, + "tests/e2e/markdown-nested-toggle.spec.ts": 50700, + "tests/e2e/markdown-ordered-list-exit.spec.ts": 53200, + "tests/e2e/markdown-prose-reflow.spec.ts": 62000, + "tests/e2e/markdown-table-row-backspace.spec.ts": 24600, + "tests/e2e/mobile-banner.spec.ts": 89300, + "tests/e2e/multi-client-navigation-isolation.spec.ts": 99900, + "tests/e2e/native-chat-ask-user-question-card.spec.ts": 23600, + "tests/e2e/native-chat-first-flush-race.spec.ts": 22700, + "tests/e2e/native-chat-history-prepend-anchor.spec.ts": 29400, + "tests/e2e/new-workspace-create-more.spec.ts": 36300, + "tests/e2e/new-workspace-cross-project-dialog.spec.ts": 17100, + "tests/e2e/new-workspace-linked-item-project-switch.spec.ts": 45200, + "tests/e2e/notification-settings.spec.ts": 23500, + "tests/e2e/onboarding.spec.ts": 276800, + "tests/e2e/orchestration-idle-mail-delivery.spec.ts": 220800, + "tests/e2e/orchestration-idle-mail-restore.spec.ts": 27000, + "tests/e2e/orchestration-legacy-worker-missing-terminal-recovery.spec.ts": 22200, + "tests/e2e/orchestration-legacy-worker-restart-recovery.spec.ts": 50700, + "tests/e2e/orchestration-low-level-dispatch-release.spec.ts": 15000, + "tests/e2e/orchestration-worker-settlement-release-cli.spec.ts": 18100, + "tests/e2e/orchestration-worker-terminal-visibility.spec.ts": 53200, + "tests/e2e/orchestration-worker-transcript-providers.spec.ts": 54700, + "tests/e2e/paired-browser-creation-reconciliation-failure.spec.ts": 21600, + "tests/e2e/paired-cli-terminal-graph-sync-tab-retention.spec.ts": 42600, + "tests/e2e/paired-client-hosted-browser-cookie-survival.spec.ts": 20100, + "tests/e2e/paired-client-hosted-browser-double-restart.spec.ts": 45800, + "tests/e2e/paired-client-hosted-browser-ghost-close.spec.ts": 48600, + "tests/e2e/paired-client-hosted-browser-host-strip.spec.ts": 23300, + "tests/e2e/paired-client-hosted-browser-quit-survival.spec.ts": 145700, + "tests/e2e/paired-client-hosted-browser-restart-survival.spec.ts": 20700, + "tests/e2e/paired-client-hosted-browser-title-hold.spec.ts": 24300, + "tests/e2e/paired-client-hosted-browser.spec.ts": 53000, + "tests/e2e/paired-cmd-j-host-qualified-tabs.spec.ts": 53300, + "tests/e2e/paired-external-worktree-discovery.spec.ts": 19300, + "tests/e2e/paired-instant-browser-tab.spec.ts": 191700, + "tests/e2e/paired-preview-address-bar-convergence.spec.ts": 162000, + "tests/e2e/paired-quick-open-large-tree.spec.ts": 26300, + "tests/e2e/paired-remote-browser-ghost-subscriber-rejoin.spec.ts": 22600, + "tests/e2e/paired-remote-browser-link-open-routing.spec.ts": 78000, + "tests/e2e/paired-remote-browser-stream-reconnect.spec.ts": 57200, + "tests/e2e/paired-remote-html-preview-local-render.spec.ts": 262700, + "tests/e2e/paired-remote-pane-layout-retry.spec.ts": 28200, + "tests/e2e/paired-remote-split-pane-host-retired-ghost.spec.ts": 20000, + "tests/e2e/paired-remote-terminal-browser-link.spec.ts": 78000, + "tests/e2e/paired-remote-terminal-host-restart-background-sync.spec.ts": 35600, + "tests/e2e/paired-remote-terminal-materialization-reconnect.spec.ts": 27100, + "tests/e2e/paired-remote-terminal-parked-reveal-interactivity.spec.ts": 39700, + "tests/e2e/paired-remote-terminal-serve-restart-binding.spec.ts": 28300, + "tests/e2e/paired-skill-installation.spec.ts": 26500, + "tests/e2e/paired-split-pane-browser-placement.spec.ts": 85900, + "tests/e2e/paired-startup-exec-readiness.spec.ts": 6000, + "tests/e2e/paired-web-add-project-unavailable-host.spec.ts": 13200, + "tests/e2e/persisted-session-production-upgrade.spec.ts": 26700, + "tests/e2e/pet-status-segment-layout.spec.ts": 25100, + "tests/e2e/pi-ui-prompt-status.spec.ts": 21100, + "tests/e2e/plugin-demo.spec.ts": 26600, + "tests/e2e/plugin-marketplace-content.spec.ts": 39600, + "tests/e2e/plugin-panel-containment.spec.ts": 50300, + "tests/e2e/plugin-startup-budget.spec.ts": 20400, + "tests/e2e/pr-comments-sidebar-cards.spec.ts": 144300, + "tests/e2e/pr11346-selected-runtime-add.spec.ts": 33800, + "tests/e2e/project-group-creation-visibility.spec.ts": 132000, + "tests/e2e/project-group-manual-sort.spec.ts": 100300, + "tests/e2e/pty-snapshot-capability-main-stall.spec.ts": 16100.000000000002, + "tests/e2e/quick-open-file-paths.spec.ts": 22800, + "tests/e2e/remote-session-bulk-open-freeze-repro.spec.ts": 51500, + "tests/e2e/renderer-blocked-navigation-graph-authority.spec.ts": 43600, + "tests/e2e/renderer-crash-recovery-terminal-input.spec.ts": 34500, + "tests/e2e/repo-icon-emoji-picker.spec.ts": 22900, + "tests/e2e/repro-7732-gitlab-checks-job-details.spec.ts": 29900, + "tests/e2e/resource-manager-unbound-session-safety.spec.ts": 18700, + "tests/e2e/resource-usage-warm-reattach.spec.ts": 18700, + "tests/e2e/restart-restore-terminal-input.spec.ts": 77800, + "tests/e2e/rich-markdown-inline-image.spec.ts": 55200, + "tests/e2e/rich-markdown-link-bubble-stacking.spec.ts": 30100, + "tests/e2e/right-sidebar-windows-titlebar.spec.ts": 14600, + "tests/e2e/runtime-host-status-recovery.spec.ts": 156000, + "tests/e2e/settings-agent-awake.spec.ts": 33400, + "tests/e2e/settings-display-name-ime.spec.ts": 18100, + "tests/e2e/settings-search-responsiveness.spec.ts": 15600, + "tests/e2e/settings-search-shortcuts-pane.spec.ts": 15800, + "tests/e2e/settings-skill-detection.spec.ts": 21800, + "tests/e2e/settled-worker-tab-survives-restart.spec.ts": 62400, + "tests/e2e/setup-guide-sidebar.spec.ts": 6800, + "tests/e2e/setup-script-import.spec.ts": 57600, + "tests/e2e/setup-script-prompt-unreadable-orca-yaml.spec.ts": 19100, + "tests/e2e/sidebar-agent-row-identity.spec.ts": 16700, + "tests/e2e/slept-workspace-remount-wake.spec.ts": 31000, + "tests/e2e/source-control-commit-draft-persistence.spec.ts": 16400, + "tests/e2e/source-control-commit-message-ai.spec.ts": 50900, + "tests/e2e/source-control-create-pr-intent-notice-layout.spec.ts": 16900, + "tests/e2e/source-control-create-pr-intent-switch.spec.ts": 41000, + "tests/e2e/source-control-create-pr.spec.ts": 37500, + "tests/e2e/source-control-discard-confirmation.spec.ts": 25900, + "tests/e2e/source-control-large-file-count.spec.ts": 54700, + "tests/e2e/source-control-pr-generation-switch.spec.ts": 146600, + "tests/e2e/source-control-pr-linked-issue-ai.spec.ts": 34200, + "tests/e2e/ssh-config-host-import.spec.ts": 204600, + "tests/e2e/ssh-config-host-picker.spec.ts": 206600, + "tests/e2e/ssh-host-form-modal.spec.ts": 94700, + "tests/e2e/status-bar-caffeinate.spec.ts": 17900, + "tests/e2e/status-bar-session-count-management-kill.spec.ts": 26000, + "tests/e2e/tab-close-navigation.spec.ts": 32100, + "tests/e2e/tab-create-entry-file-paths.spec.ts": 23600, + "tests/e2e/tab-rename-paste-ownership.spec.ts": 15600, + "tests/e2e/tab-rename.spec.ts": 149900, + "tests/e2e/tab-sidebar-closed-overlap.spec.ts": 28600, + "tests/e2e/tabs.spec.ts": 164500, + "tests/e2e/tasks-page.spec.ts": 90400, + "tests/e2e/terminal-attention.spec.ts": 69700, + "tests/e2e/terminal-cjk-ime-committed-text.spec.ts": 122400, + "tests/e2e/terminal-codex-hidden-startup-background.spec.ts": 17500, + "tests/e2e/terminal-codex-home.spec.ts": 14600, + "tests/e2e/terminal-cold-activation-deferral.spec.ts": 22200, + "tests/e2e/terminal-column-desync-repro.spec.ts": 149400, + "tests/e2e/terminal-context-menu-session-id.spec.ts": 20800, + "tests/e2e/terminal-cursor-inactive-style.spec.ts": 15700, + "tests/e2e/terminal-duplicate-pty-renderer-reveal.spec.ts": 23000, + "tests/e2e/terminal-foreground-redraw-freeze.spec.ts": 14800, + "tests/e2e/terminal-hangul-wrap-boundary-bytes.spec.ts": 107100, + "tests/e2e/terminal-hidden-child-tui-kill-mode-reset.spec.ts": 76200, + "tests/e2e/terminal-hidden-tui-visual-restore.spec.ts": 70600, + "tests/e2e/terminal-hidden-view-parking.spec.ts": 199400, + "tests/e2e/terminal-history-size-typing-latency.spec.ts": 21200, + "tests/e2e/terminal-ime-exact-byte.spec.ts": 60400, + "tests/e2e/terminal-inline-tui-reveal-convergence.spec.ts": 309900, + "tests/e2e/terminal-korean-composing-chord-order.spec.ts": 16400, + "tests/e2e/terminal-korean-endofrow-preedit-cell-span.spec.ts": 29800, + "tests/e2e/terminal-korean-midline-preedit-occlusion.spec.ts": 45100, + "tests/e2e/terminal-korean-preedit-visibility.spec.ts": 62200, + "tests/e2e/terminal-large-paste-responsiveness.spec.ts": 15400, + "tests/e2e/terminal-link-click-ownership.spec.ts": 69100, + "tests/e2e/terminal-link-hover-after-worktree-return.spec.ts": 46600, + "tests/e2e/terminal-long-table-scroll-restore.spec.ts": 74600, + "tests/e2e/terminal-macos-system-key-remap.spec.ts": 97000, + "tests/e2e/terminal-opencode-emoji-table-rendering.spec.ts": 21600, + "tests/e2e/terminal-osc-color-queries.spec.ts": 14900, + "tests/e2e/terminal-osc8-cold-park-restore.spec.ts": 19000, + "tests/e2e/terminal-output-scheduler.spec.ts": 54700, + "tests/e2e/terminal-pane-binding-lifecycle.spec.ts": 46800, + "tests/e2e/terminal-pane-close-layout-consistency.spec.ts": 128900, + "tests/e2e/terminal-pane-content-retention.spec.ts": 45100, + "tests/e2e/terminal-pane-layout-resize.spec.ts": 47500, + "tests/e2e/terminal-pane-split-identity.spec.ts": 81000, + "tests/e2e/terminal-pane-title-editing.spec.ts": 189100, + "tests/e2e/terminal-pane-title-focus-handoff.spec.ts": 158000, + "tests/e2e/terminal-pane-title-strip-placement.spec.ts": 62400, + "tests/e2e/terminal-parked-cli-split.spec.ts": 28900, + "tests/e2e/terminal-parked-close-retirement.spec.ts": 16300, + "tests/e2e/terminal-parked-memory.spec.ts": 80100, + "tests/e2e/terminal-paste-ownership.spec.ts": 66100, + "tests/e2e/terminal-pinned-viewport-streaming-switch.spec.ts": 19400, + "tests/e2e/terminal-pinned-viewport-worktree-switch.spec.ts": 16700, + "tests/e2e/terminal-push-delivery-loss-recovery.spec.ts": 16500, + "tests/e2e/terminal-quick-command-pre-bind-recovery.spec.ts": 42100, + "tests/e2e/terminal-raw-emoji-table-scroll-restore.spec.ts": 35800, + "tests/e2e/terminal-reattach-mouse-mode-leak.spec.ts": 22800, + "tests/e2e/terminal-reattach-tui-mouse-mode.spec.ts": 44200, + "tests/e2e/terminal-restart-persistence.spec.ts": 109600, + "tests/e2e/terminal-reveal-paused-render-repro.spec.ts": 31100, + "tests/e2e/terminal-reveal-stale-pty-resize-repro.spec.ts": 49000, + "tests/e2e/terminal-scroll-intent-follow.spec.ts": 73200, + "tests/e2e/terminal-send-agent-prompt-submit.spec.ts": 51400, + "tests/e2e/terminal-shortcuts.spec.ts": 91700, + "tests/e2e/terminal-sleep-wake-restore.spec.ts": 16800, + "tests/e2e/terminal-split-pane-paste-ownership.spec.ts": 30700, + "tests/e2e/terminal-streaming-refocus-viewport.spec.ts": 15800, + "tests/e2e/terminal-stuck-occlusion-recovery.spec.ts": 15200, + "tests/e2e/terminal-tab-close-restart-persistence.spec.ts": 22800, + "tests/e2e/terminal-tab-close-running-confirm-mouse.spec.ts": 57100, + "tests/e2e/terminal-tab-close-running-confirm.spec.ts": 27500, + "tests/e2e/terminal-tab-switch-sigwinch-restore.spec.ts": 30400, + "tests/e2e/terminal-tab-switch-visual-restore.spec.ts": 90300, + "tests/e2e/terminal-tui-wheel-drain.spec.ts": 528000, + "tests/e2e/terminal-tui-wheel-reports.spec.ts": 34400, + "tests/e2e/terminal-typing-latency.spec.ts": 15200, + "tests/e2e/terminal-wedged-write-pipeline-recovery.spec.ts": 71900, + "tests/e2e/terminal-window-wake-stale-grid-repro.spec.ts": 15900, + "tests/e2e/update-install-renderer-checkpoint-recovery.spec.ts": 14900, + "tests/e2e/usage-overview.spec.ts": 33800, + "tests/e2e/voice-microphone-selection.spec.ts": 34000, + "tests/e2e/windows-terminal-env-icons.spec.ts": 15400, + "tests/e2e/workspace-back-forward-navigation.spec.ts": 108500, + "tests/e2e/workspace-board-lane-virtualization.spec.ts": 107700, + "tests/e2e/workspace-emoji-picker.spec.ts": 32900, + "tests/e2e/workspace-session-corrupt-tab-salvage.spec.ts": 21100, + "tests/e2e/workspace-space-git-status.spec.ts": 16100.000000000002, + "tests/e2e/worktree-active-delete-scroll-position.spec.ts": 59200, + "tests/e2e/worktree-card-downward-drag.spec.ts": 60000, + "tests/e2e/worktree-delete-shortcut.spec.ts": 20600, + "tests/e2e/worktree-jump-palette-filter.spec.ts": 96200, + "tests/e2e/worktree-lifecycle.spec.ts": 44000, + "tests/e2e/worktree-lineage-agent-expansion.spec.ts": 21800, + "tests/e2e/worktree-lineage.spec.ts": 96700, + "tests/e2e/worktree-recent-sort.spec.ts": 28400, + "tests/e2e/worktree-scroll-to-current.spec.ts": 62400, + "tests/e2e/worktree-smart-sort.spec.ts": 14100, + "tests/e2e/worktree-switch-first-paint.spec.ts": 52200, + "tests/e2e/worktree-switch-responsiveness.spec.ts": 15100, + "tests/e2e/worktree.spec.ts": 118100 + } + } +} diff --git a/config/scripts/ci-shard-timings.md b/config/scripts/ci-shard-timings.md new file mode 100644 index 00000000000..f6e846cfd6d --- /dev/null +++ b/config/scripts/ci-shard-timings.md @@ -0,0 +1,93 @@ +# Timing-based CI shards + +The eight unit shards and fourteen general E2E shards use longest-processing-time +assignment of whole files to the currently lightest shard. Ties use file path and +then shard index, independent of filesystem enumeration and locale. Unknown, +zero, or invalid durations use the baseline's positive median (1 second when no +positive evidence exists). Deleted files never enter discovery. Unit weights add +526ms per file for measured transform/setup/import/environment overhead. + +Unit assignment runs inside Vitest's sequencer after discovery and CLI exclusions; +Vitest's default sort, workers and isolation remain intact. It is enabled only by +`ORCA_BALANCE_UNIT_SHARDS=1`; ordinary local runs and explicit file filters retain +their existing behavior. E2E uses Playwright's native `--list` and `--test-list`, +retaining project filters, skipped tests and complete serial groups within files. +The workflow verifies selected test IDs against full discovery before executing. +Dedicated SSH, native IME, WSL and first-paint lanes are unchanged. + +## Evidence and limits + +`ci-shard-timings.json` records run IDs and every contributing job ID: + +- Unit run **34675583768**, Node 24, all eight successful shards: 8,484 completed + file durations. The summed transform/setup/import/environment durations divided + by measured file count give a rounded-up **526ms** per-file overhead allowance. + The original shard weighted loads were **764–849 worker-seconds**, versus + **792–792** after balancing the identical measured files. File counts change + from **1,056–1,065** to **1,060–1,061**. +- General E2E run **34652504501**, all fourteen shard logs: 291 files with completed + headless test durations, including failures. Headful benchmark reruns are not + counted. Original completed test loads were **540–1,727 seconds**, versus + **1,083–1,093** after whole-file balancing on the same measured files. The longest + measured file is **528 seconds**, below the balanced shard load. +- Current checkout discovery at validation contained **8,553 unit files** after the + workflow's exact exclusions and **733 headless E2E tests in 340 files**. New and + unmeasured files remain selected. Projected current loads were about **797 + worker-seconds** per unit shard (1,068–1,070 files) and **1,190–1,200 seconds** per + E2E shard (22–25 files). + +These are scheduling projections, not measured post-change wall-clock gains. +Unit durations overlap across workers and the overhead allowance is an average, +not a per-file import profile. E2E evidence includes failed shards and can omit +unfinished tests; unknowns receive a deterministic estimate. Historical timings +age as specs change. Full CI runs on the existing runner classes are required to +measure elapsed-time and occupancy improvements, including discovery overhead. +No retries, assertions, coverage exclusions, runner classes or shard counts changed. + +## Reproduction and refresh + +Every shard uploads an artifact named with its shard, Node version where relevant, +and run attempt. `assignment.json` contains the checked-out source SHA, run ID, +attempt, baseline SHA-256, algorithm, fallback, all shard files and chosen shard. +E2E also retains both discovery reports and `selected.txt`. Artifacts live for +14 days. A rerun of the same source uses the same checked-in baseline rather than +mutable timing caches; a GitHub job rerun therefore keeps its assignment. + +For E2E reproduction, check out the recorded source and pass the saved list to the +existing command: `pnpm run test:e2e --test-list=/path/to/selected.txt` with the same +CI environment/build inputs. For unit reproduction, use the unchanged workflow +command and exclusions with `ORCA_BALANCE_UNIT_SHARDS=1` and the recorded +`--shard=INDEX/8`. Direct test-file reruns remain supported. + +To refresh the baseline, download `log-JOB_ID.txt` files into one directory from +exactly one eight-shard unit run and one fourteen-shard general E2E run. Use the +job IDs from the Actions jobs API and fetch each with +`gh api repos/stablyai/orca/actions/jobs/JOB_ID/logs`. Do not include dedicated +lanes or multiple attempts. Then run: + +```sh +node config/scripts/ci-shard-timing-import.mjs LOG_DIRECTORY UNIT_RUN_ID E2E_RUN_ID config/scripts/ci-shard-timings.json +``` + +The initial source logs are in `/tmp/orca-ci-shard-logs`; two were reused from +`/tmp/orca-ci-audit`, and the remaining twenty were fetched read-only. Reimporting +those logs reproduced the checked-in JSON byte-for-byte. Review file-count and +load projections before adopting a new baseline; no network access is needed to +plan or run shards. + +## Validation + +- 74 focused tests passed across the two new test files and existing PR + parallelism, E2E gate and release E2E dispatch contracts. +- The pinned Playwright CLI selected the real 733-test suite across all fourteen + saved test lists with exact-once identity coverage and no missing tests. +- A temporary native Playwright fixture checks fourteen shards, serial groups, + skipped cases, headful filtering and mismatch rejection without launching UI. +- Real Vitest discovery with all workflow exclusions yielded 8,553 files; the + sequencer's eight assignments covered each exactly once. An actual opt-in + Vitest shard executed successfully and persisted its manifest. +- Focused TypeScript checking of `config/vitest.config.ts` and imported modules, + oxlint, formatting and baseline reimport checks passed. + +All local tests used `ORCA_BACKGROUND_LAUNCH=1` in background tool sessions. No app +windows or full E2E test bodies were launched. diff --git a/config/scripts/ci-unit-sequencer.mjs b/config/scripts/ci-unit-sequencer.mjs new file mode 100644 index 00000000000..cd55b3343b2 --- /dev/null +++ b/config/scripts/ci-unit-sequencer.mjs @@ -0,0 +1,19 @@ +import { relative } from 'node:path' +import { BaseSequencer } from 'vitest/node' +import { balanceFiles, readTimingBaseline, writeAssignment } from './ci-shard-assignment.mjs' + +export default class TimingSequencer extends BaseSequencer { + async shard(specs) { + const { index, count } = this.ctx.config.shard + const key = (spec) => relative(this.ctx.config.root, spec.moduleId).replaceAll('\\', '/') + const baseline = readTimingBaseline('unit') + const assignment = balanceFiles(specs.map(key), count, baseline.timings, baseline.overheadMs) + writeAssignment(process.env.ORCA_SHARD_MANIFEST ?? 'ci-shards/unit-assignment.json', { + ...assignment, + baselineSha256: baseline.baselineSha256, + selectedShard: index + }) + const selected = new Set(assignment.shards[index - 1].files) + return specs.filter((spec) => selected.has(key(spec))) + } +} diff --git a/config/scripts/electron-vite-output-contract.test.ts b/config/scripts/electron-vite-output-contract.test.ts index a7ac04a1a09..285b58f2ac7 100644 --- a/config/scripts/electron-vite-output-contract.test.ts +++ b/config/scripts/electron-vite-output-contract.test.ts @@ -116,9 +116,9 @@ describe('Electron Vite output contract', () => { expect(external('node:fs', undefined, false)).toBe(true) expect(external('@xterm/headless', undefined, false)).toBe(false) expect(external('@xterm/addon-serialize', undefined, false)).toBe(false) - expect(external('psl', undefined, false)).toBe(false) + expect(external('tldts', undefined, false)).toBe(false) expect(external('zod', undefined, false)).toBe(false) - expect(electronViteConfig.main?.build?.externalizeDeps?.exclude).toContain('psl') + expect(electronViteConfig.main?.build?.externalizeDeps?.exclude).toContain('tldts') expect(electronViteConfig.main?.build?.externalizeDeps?.exclude).toContain('zod') }) diff --git a/config/scripts/ensure-native-runtime.mjs b/config/scripts/ensure-native-runtime.mjs index 10e8426c2a5..6278a1e0b41 100644 --- a/config/scripts/ensure-native-runtime.mjs +++ b/config/scripts/ensure-native-runtime.mjs @@ -22,7 +22,7 @@ const runtime = readRuntimeArg() const NATIVE_MODULES = [ 'node-pty', ...(process.platform === 'win32' - ? ['windows-native-registry', '@vscode/windows-process-tree'] + ? ['@orca/windows-registry', '@vscode/windows-process-tree'] : []) ] const NODE_PTY_CONPTY_RUNTIME_FILES = ['conpty.dll', 'OpenConsole.exe'] @@ -275,7 +275,7 @@ function loadNativeModule(moduleName) { } return } - if (moduleName === 'windows-native-registry') { + if (moduleName === '@orca/windows-registry') { const registry = require(moduleName) // Why: the package defers loading its .node addon until the first registry call. registry.getRegistryKey(registry.HK.CU, 'Environment') diff --git a/config/scripts/ensure-native-runtime.test.mjs b/config/scripts/ensure-native-runtime.test.mjs index 1e7d888d2e2..47fe435edcc 100644 --- a/config/scripts/ensure-native-runtime.test.mjs +++ b/config/scripts/ensure-native-runtime.test.mjs @@ -87,7 +87,7 @@ describe('ensure-native-runtime', () => { const log = readFileSync(logPath, 'utf8') expect(log.match(/pnpm exec node-gyp rebuild\n/g)).toHaveLength(2) expect(log).toContain(join('node_modules', 'node-pty')) - expect(log).toContain(join('node_modules', 'windows-native-registry')) + expect(log).toContain(join('node_modules', '@orca', 'windows-registry')) } finally { rmSync(projectDir, { recursive: true, force: true }) } @@ -299,11 +299,11 @@ function writeFakeWindowsRegistry(projectDir, { requiresMarker = false } = {}) { if (process.platform !== 'win32') { return } - const registryDir = join(projectDir, 'node_modules', 'windows-native-registry') + const registryDir = join(projectDir, 'node_modules', '@orca', 'windows-registry') mkdirSync(registryDir, { recursive: true }) writeFileSync( join(registryDir, 'package.json'), - '{"name":"windows-native-registry","version":"3.2.2","main":"index.js"}\n' + '{"name":"@orca/windows-registry","version":"1.0.0","main":"index.js"}\n' ) const markerGate = requiresMarker ? `if (!require('node:fs').existsSync(process.env.ORCA_NATIVE_TEST_MARKER)) { throw new Error('registry ABI mismatch sentinel') }` diff --git a/config/scripts/generate-rpc-params-catalog.mjs b/config/scripts/generate-rpc-params-catalog.mjs index acf31aae6a1..e42ea39beb1 100644 --- a/config/scripts/generate-rpc-params-catalog.mjs +++ b/config/scripts/generate-rpc-params-catalog.mjs @@ -197,9 +197,10 @@ ${uncataloged.map((name) => ` '${name}'`).join(',\n')} export type RpcMethodName = keyof typeof RPC_PARAMS_BY_METHOD -// Why: z.output is the post-parse shape the handler receives. z.input is not a -// send-side type here — requiredString is z.unknown().transform(...), so its input -// admits any value and loses optional/default semantics. +// Why: z.output is the post-parse shape the handler receives, which is not what a +// client may send — a .default() field reads as required. z.input is not the answer +// either: requiredString is z.unknown().transform(...), so its input admits any value. +// Senders use RpcSendParams from ./rpc-send-params, which is derived from this map. export type RpcParams = (typeof RPC_PARAMS_BY_METHOD)[Method] extends z.ZodType ? z.output<(typeof RPC_PARAMS_BY_METHOD)[Method]> diff --git a/config/scripts/generate-skill-bundle-manifest.mjs b/config/scripts/generate-skill-bundle-manifest.mjs index 226ea74c4fb..a84923e762d 100644 --- a/config/scripts/generate-skill-bundle-manifest.mjs +++ b/config/scripts/generate-skill-bundle-manifest.mjs @@ -6,8 +6,7 @@ import path from 'node:path' import process from 'node:process' import { isDeepStrictEqual } from 'node:util' -// Why: the three artifacts version independently — bumping one shape must not -// rewrite the others or bypass the registry's schema-gated append-only guard. +// Version artifacts independently to preserve the registry's schema-gated append-only guard. const CURRENT_MANIFEST_SCHEMA_VERSION = 2 const SNAPSHOT_REGISTRY_SCHEMA_VERSION = 1 const RELEASE_MAPPING_SCHEMA_VERSION = 1 @@ -41,18 +40,19 @@ function normalizeText(bytes) { return Buffer.from(text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'), 'utf8') } -function classifyFile(bytes) { +function normalizedTextOrNull(bytes) { if (bytes.includes(0)) { - return 'binary' + return null } try { - normalizeText(bytes) - return 'text' + return normalizeText(bytes) } catch { - return 'binary' + return null } } +const classifyFile = (bytes) => (normalizedTextOrNull(bytes) === null ? 'binary' : 'text') + function assertSafeRelativePath(relativePath) { if ( path.isAbsolute(relativePath) || @@ -64,17 +64,17 @@ function assertSafeRelativePath(relativePath) { } function describeFile(manifestPath, bytes, executable) { - const classification = classifyFile(bytes) + const normalized = normalizedTextOrNull(bytes) const exactSha256 = sha256(bytes) - const textNormalizedSha256 = classification === 'text' ? sha256(normalizeText(bytes)) : null + const textNormalizedSha256 = normalized === null ? null : sha256(normalized) return { path: manifestPath, size: bytes.length, executable, - classification, + classification: normalized === null ? 'binary' : 'text', exactSha256, textNormalizedSha256, - identitySha256: classification === 'text' && !executable ? textNormalizedSha256 : exactSha256, + identitySha256: normalized !== null && !executable ? textNormalizedSha256 : exactSha256, gitBlobSha: gitObjectSha('blob', bytes).toString('hex') } } diff --git a/config/scripts/headless-serve-shutdown-matrix.test.mjs b/config/scripts/headless-serve-shutdown-matrix.test.mjs new file mode 100644 index 00000000000..7231cc21e4f --- /dev/null +++ b/config/scripts/headless-serve-shutdown-matrix.test.mjs @@ -0,0 +1,140 @@ +import { createHash } from 'node:crypto' +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +const { spawnSync } = vi.hoisted(() => ({ spawnSync: vi.fn() })) +vi.mock('node:child_process', () => ({ spawnSync })) + +let directory +let artifact +let originalArgv +let originalExitCode +const commands = () => spawnSync.mock.calls.map(([, args]) => args) +const signalRuns = () => commands().filter((args) => ['INT', 'TERM'].includes(args.at(-1))) +const succeeded = { status: 0, stdout: '', stderr: '' } + +async function run(...options) { + process.argv = ['node', 'runner', '--appimage', artifact, ...options] + await import('./run-headless-serve-shutdown-docker.mjs') +} + +beforeEach(() => { + vi.resetModules() + spawnSync.mockReset().mockReturnValue(succeeded) + vi.spyOn(console, 'log').mockImplementation(() => {}) + vi.spyOn(console, 'error').mockImplementation(() => {}) + directory = mkdtempSync(join(tmpdir(), 'orca-shutdown-matrix-')) + artifact = join(directory, 'original.AppImage') + writeFileSync(artifact, 'original package bytes') + originalArgv = process.argv + originalExitCode = process.exitCode +}) + +afterEach(() => { + process.argv = originalArgv + process.exitCode = originalExitCode + rmSync(directory, { recursive: true, force: true }) + vi.restoreAllMocks() +}) + +describe('packaged shutdown matrix', () => { + it('shares extraction but isolates every entrypoint and signal', async () => { + await run('--all-entrypoints') + expect(commands().filter((args) => args[0] === 'build')).toHaveLength(1) + const startup = commands().filter((args) => + args.includes('/usr/local/bin/run-appimage-desktop-startup-case') + ) + const extraction = commands().filter((args) => + args.some((arg) => arg.includes('120s /input/orca.AppImage --appimage-extract')) + ) + expect(startup).toHaveLength(1) + expect(extraction).toHaveLength(1) + expect(commands().indexOf(startup[0])).toBeLessThan(commands().indexOf(extraction[0])) + expect(signalRuns()).toHaveLength(6) + const names = new Set() + for (const [index, args] of signalRuns().entries()) { + const entrypoint = ['app', 'launcher', 'appimage'][Math.floor(index / 2)] + expect(args).toContain(`ORCA_TEST_ENTRYPOINT=${entrypoint}`) + expect(args).toContain( + `ORCA_SIGNAL_TARGET=${entrypoint === 'appimage' ? 'serving-electron' : 'app'}` + ) + expect(args).toContain( + `ORCA_INT_DELIVERY=${entrypoint === 'appimage' ? 'pid' : 'foreground-process-group'}` + ) + expect(args.at(-1)).toBe(index % 2 === 0 ? 'INT' : 'TERM') + expect(args).toContain(`${artifact}:/input/orca.AppImage:ro`) + expect(args.some((arg) => arg.endsWith(':/artifacts:ro'))).toBe(true) + expect(args).toContain('--rm') + names.add(args[args.indexOf('--name') + 1]) + } + expect(names.size).toBe(6) + const evidence = console.log.mock.calls + .map(([line]) => line) + .filter((line) => line.startsWith('{')) + .map(JSON.parse) + expect(evidence).toHaveLength(3) + expect( + evidence.every( + (entry) => + entry.sha256 === createHash('sha256').update('original package bytes').digest('hex') + ) + ).toBe(true) + expect( + commands() + .slice(-2) + .map((args) => args.slice(0, 2)) + ).toEqual([ + ['volume', 'rm'], + ['image', 'rm'] + ]) + }) + + it('attributes failures and still attempts later cases before cleanup', async () => { + spawnSync.mockImplementation((_, args) => + args.at(-1) === 'INT' ? { ...succeeded, status: 7 } : succeeded + ) + await expect(run('--all-entrypoints')).rejects.toThrow( + 'app:INT:7, launcher:INT:7, appimage:INT:7' + ) + expect(signalRuns()).toHaveLength(6) + expect(commands().at(-2).slice(0, 2)).toEqual(['volume', 'rm']) + }) + + it('cleans setup resources without running cases after failed extraction', async () => { + spawnSync.mockImplementation((_, args) => + args.some((arg) => arg.includes('120s /input/orca.AppImage --appimage-extract')) + ? { ...succeeded, status: 9 } + : succeeded + ) + await expect(run('--all-entrypoints')).rejects.toThrow('docker run failed') + expect(signalRuns()).toHaveLength(0) + expect( + commands() + .slice(-2) + .map((args) => args.slice(0, 2)) + ).toEqual([ + ['volume', 'rm'], + ['image', 'rm'] + ]) + }) + + it('preserves individual launcher overlay invocations', async () => { + await run('--entrypoint', 'launcher', '--launcher-exec-overlay') + expect(signalRuns()).toHaveLength(2) + expect(signalRuns().every((args) => args.includes('ORCA_TEST_ENTRYPOINT=launcher'))).toBe(true) + expect( + commands().some((args) => + args.some((arg) => arg.includes("sed -i 's/^ELECTRON_RUN_AS_NODE=1")) + ) + ).toBe(true) + }) + + it('rejects ambiguous matrix overrides before invoking Docker', async () => { + await expect(run('--all-entrypoints', '--entrypoint', 'launcher')).rejects.toThrow( + 'cannot be combined' + ) + expect(spawnSync).not.toHaveBeenCalled() + }) +}) diff --git a/config/scripts/headless-serve-shutdown-workflow.test.mjs b/config/scripts/headless-serve-shutdown-workflow.test.mjs index 90a3f73c77d..3b71a2e774e 100644 --- a/config/scripts/headless-serve-shutdown-workflow.test.mjs +++ b/config/scripts/headless-serve-shutdown-workflow.test.mjs @@ -56,12 +56,6 @@ describe('headless serve shutdown PR gate', () => { const packageStep = steps.find((step) => step.name === 'Package unpacked app') const markerStep = steps.find((step) => step.name === 'Verify root-package marker payloads') const shutdownStep = steps.find((step) => step.name === 'Verify headless serve signal shutdown') - const launcherShutdownStep = steps.find( - (step) => step.name === 'Verify extracted launcher serve signal shutdown' - ) - const appImageShutdownStep = steps.find( - (step) => step.name === 'Verify AppImage CLI registration and serve signal shutdown' - ) expect(workflow.jobs.package['timeout-minutes']).toBe(90) expect(packageStep.run).toContain('--linux AppImage deb rpm --x64 --publish never') @@ -69,19 +63,13 @@ describe('headless serve shutdown PR gate', () => { expect(markerStep.run).toContain('rpm2cpio') expect(steps.indexOf(markerStep)).toBeGreaterThan(steps.indexOf(packageStep)) expect(shutdownStep.run).toBe( - 'node config/scripts/run-headless-serve-shutdown-docker.mjs --appimage dist/orca-linux.AppImage' + 'node config/scripts/run-headless-serve-shutdown-docker.mjs --appimage dist/orca-linux.AppImage --all-entrypoints' ) - expect(launcherShutdownStep.run).toContain( - 'node config/scripts/run-headless-serve-shutdown-docker.mjs' - ) - expect(launcherShutdownStep.run).toContain('--entrypoint launcher') - expect(appImageShutdownStep.run).toContain('--entrypoint appimage') - expect(appImageShutdownStep.run).toContain('--signal-target serving-electron') - expect(appImageShutdownStep.run).toContain('--int-delivery pid') expect(steps.indexOf(shutdownStep)).toBeGreaterThan(steps.indexOf(packageStep)) expect(steps.indexOf(shutdownStep)).toBeGreaterThan(steps.indexOf(markerStep)) - expect(steps.indexOf(launcherShutdownStep)).toBeGreaterThan(steps.indexOf(shutdownStep)) - expect(steps.indexOf(appImageShutdownStep)).toBeGreaterThan(steps.indexOf(launcherShutdownStep)) + expect( + steps.filter((step) => step.run?.includes('run-headless-serve-shutdown-docker.mjs')) + ).toHaveLength(1) }) it('keeps readiness polling finite and leak-free', () => { diff --git a/config/scripts/hermes-run-correlation-benchmark.mjs b/config/scripts/hermes-run-correlation-benchmark.mjs new file mode 100644 index 00000000000..364774577cf --- /dev/null +++ b/config/scripts/hermes-run-correlation-benchmark.mjs @@ -0,0 +1,136 @@ +#!/usr/bin/env node +import assert from 'node:assert/strict' +import { readFile, mkdtemp, rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join, basename } from 'node:path' +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' + +const root = fileURLToPath(new URL('../..', import.meta.url)) +const fixture = await mkdtemp(join(tmpdir(), 'orca-hermes-correlation-')) +const baselineDirectory = process.argv[2] +const key = (seconds) => + new Date(Date.UTC(2026, 0, 1) + seconds * 1000) + .toISOString() + .replace(/[-:]/g, '') + .replace('T', '_') + .slice(0, 15) +try { + for (const host of ['native', 'relay']) { + const entry = + host === 'native' + ? 'src/main/automations/hermes-cron-run-content.ts' + : 'src/relay/hermes-run-correlation.ts' + const readers = [] + for (const mode of baselineDirectory ? ['baseline', 'current'] : ['current']) { + const bundle = join(fixture, `${host}-${mode}.cjs`) + await build({ + entryPoints: [join(root, entry)], + bundle: true, + platform: 'node', + format: 'cjs', + outfile: bundle, + plugins: + mode === 'baseline' + ? [ + { + name: 'baseline-correlation', + setup(builder) { + builder.onLoad( + { filter: /hermes-(cron-run-content|run-correlation)\.ts$/ }, + async (args) => ({ + contents: await readFile( + join(baselineDirectory, basename(args.path)), + 'utf8' + ), + loader: 'ts' + }) + ) + } + } + ] + : [] + }) + readers.push({ mode, ...createRequire(import.meta.url)(bundle) }) + } + if (readers.length === 2) { + let seed = 92817 + const random = (max) => { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return seed % max + } + const pool = [ + null, + '', + 'invalid', + '20260101_000000', + '20260101_000200', + '20260102_000000', + '20260103_000000', + '20260101_240000' + ] + for (let trial = 0; trial < 200; trial++) { + const sessions = Array.from({ length: random(70) }, (_, i) => ({ + kind: 'session', + id: `session-${i}`, + job_id: 'job', + run_at: null, + run_key: pool[random(pool.length)], + output_content: `session ${i}` + })) + const outputs = Array.from({ length: random(70) }, (_, i) => ({ + kind: 'output', + id: `output-${i}`, + job_id: 'job', + run_at: null, + run_key: pool[random(pool.length)], + output_path: 'unused', + output_content: `output ${i}` + })) + for (const method of [ + 'mergeHermesOutputAndSessionRunRefs', + 'mergeHermesOutputAndSessionRuns' + ]) { + assert.deepEqual( + readers[1][method](outputs, sessions), + readers[0][method](outputs, sessions) + ) + } + } + console.log(JSON.stringify({ host, randomizedParityCases: 400 })) + } + for (const runs of [100, 1000, 5000]) { + const sessions = Array.from({ length: runs }, (_, i) => ({ + kind: 'session', + id: `session-${i}`, + job_id: 'job', + run_at: null, + run_key: key(i * 3600) + })).toReversed() + const outputs = Array.from({ length: runs }, (_, i) => ({ + kind: 'output', + id: `output-${i}`, + job_id: 'job', + run_at: null, + run_key: key(i * 3600 + 120), + output_path: 'unused' + })) + let expected + for (const reader of [...readers, ...readers.toReversed()]) { + const start = performance.now() + const result = reader.mergeHermesOutputAndSessionRunRefs(outputs, sessions) + const durationMs = performance.now() - start + assert.equal(result.length, runs) + result.forEach((row, i) => assert.equal(row.session.id, `session-${i}`)) + if (expected) { + assert.deepEqual(result, expected) + } + expected = result + console.log(JSON.stringify({ host, mode: reader.mode, runs, durationMs })) + } + } + } +} finally { + await rm(fixture, { recursive: true, force: true }) +} diff --git a/config/scripts/journal-replay-retention-benchmark.mjs b/config/scripts/journal-replay-retention-benchmark.mjs new file mode 100644 index 00000000000..3991a886ab5 --- /dev/null +++ b/config/scripts/journal-replay-retention-benchmark.mjs @@ -0,0 +1,122 @@ +#!/usr/bin/env node +import assert from 'node:assert/strict' +import { mkdtemp, readFile, rm, stat } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { basename, dirname, join } from 'node:path' +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' + +// Pass a directory containing journal-open.ts and journal-row-table.ts from the base commit. +const baselineDir = process.argv[2] +assert.ok( + baselineDir, + 'Usage: node --expose-gc journal-replay-retention-benchmark.mjs BASELINE_DIR' +) +assert.ok(global.gc, 'Run with --expose-gc to measure live backing memory during replay') +const root = fileURLToPath(new URL('../..', import.meta.url)) +const fixture = await mkdtemp(join(tmpdir(), 'orca-journal-replay-bench-')) +try { + const implementations = {} + for (const arm of ['baseline', 'current']) { + const outfile = join(fixture, `${arm}.cjs`) + await build({ + stdin: { + contents: + "export {openAgentSessionJournal} from './src/main/native-chat/agent-session-journal/journal-store-factory'; export {loadJournal} from './src/main/native-chat/agent-session-journal/journal-open'; export {journalDatabaseFile} from './src/main/native-chat/agent-session-journal/journal-paths';", + resolveDir: root + }, + bundle: true, + platform: 'node', + format: 'cjs', + outfile, + plugins: [ + { + name: 'replay-memory-probe', + setup(plugin) { + plugin.onLoad( + { filter: /journal-(?:open|row-table|reducer)\.ts$/ }, + async ({ path }) => { + const leaf = basename(path) + let source = await readFile( + arm === 'baseline' && leaf !== 'journal-reducer.ts' + ? join(baselineDir, leaf) + : path, + 'utf8' + ) + if (leaf === 'journal-reducer.ts') { + const marker = + 'export function applyJournalRow(state: JournalReducerState, row: JournalRow): void {' + assert.ok(source.includes(marker)) + source = source.replace( + marker, + `${marker}\nglobalThis.__replayMemoryProbe?.(row.seq);` + ) + } + return { contents: source, loader: 'ts', resolveDir: dirname(path) } + } + ) + } + } + ] + }) + implementations[arm] = createRequire(import.meta.url)(outfile) + } + const identity = { + sessionId: 'benchmark', + workspaceId: 'fixture', + hostId: 'local', + agent: 'codex', + providerHandle: { kind: 'codex', threadId: 'thread' } + } + const journalDir = join(fixture, 'session') + const journal = await implementations.current.openAgentSessionJournal({ identity, journalDir }) + const item = { provider: 'codex', threadId: 'thread', turnId: 'turn', ordinal: 0 } + const text = 'x'.repeat(32768) + for (let revision = 0; revision < 2000; revision++) { + await journal.appendItem( + item, + { + kind: 'message', + role: 'assistant', + blocks: [{ type: 'text', text: `${text}${revision}` }] + }, + { fence: 1 } + ) + } + await journal.close() + for (const arm of ['baseline', 'current', 'current', 'baseline']) { + global.gc() + const start = performance.now() + let loaded = implementations[arm].loadJournal(journalDir, identity.sessionId) + const ms = performance.now() - start + assert.equal(loaded.state.items.size, 1) + assert.equal([...loaded.state.items.values()][0].revision, 2000) + loaded = null + global.gc() + const initialHeap = process.memoryUsage().heapUsed + let peakLiveHeap = initialHeap + globalThis.__replayMemoryProbe = (sequence) => { + if (sequence !== 1 && sequence % 256 !== 0) { + return + } + global.gc() + peakLiveHeap = Math.max(peakLiveHeap, process.memoryUsage().heapUsed) + } + loaded = implementations[arm].loadJournal(journalDir, identity.sessionId) + delete globalThis.__replayMemoryProbe + assert.equal(loaded.state.items.size, 1) + loaded = null + console.log( + JSON.stringify({ + arm, + ms, + databaseBytes: (await stat(implementations[arm].journalDatabaseFile(journalDir))).size, + peakLiveHeapDelta: peakLiveHeap - initialHeap + }) + ) + } +} finally { + delete globalThis.__replayMemoryProbe + await rm(fixture, { recursive: true, force: true }) +} diff --git a/config/scripts/legacy-alt-screen-scan-benchmark.mjs b/config/scripts/legacy-alt-screen-scan-benchmark.mjs new file mode 100644 index 00000000000..29c9621fbda --- /dev/null +++ b/config/scripts/legacy-alt-screen-scan-benchmark.mjs @@ -0,0 +1,134 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const modulePath = 'src/main/daemon/terminal-history-legacy-scrollback-restore.ts' +const baselineSource = readFileSync(0, 'utf8') +assert.ok( + baselineSource.includes('function truncateAltScreen'), + 'Pipe the baseline module on stdin' +) +const arms = {} +for (const [name, source] of [ + ['baseline', baselineSource], + ['indexed', readFileSync(modulePath, 'utf8')] +]) { + const result = await build({ + stdin: { + contents: `export { truncateAltScreen } from './${modulePath}'`, + resolveDir: process.cwd(), + loader: 'ts' + }, + bundle: true, + format: 'esm', + platform: 'node', + write: false, + plugins: [ + { + name: 'private-export', + setup(api) { + api.onLoad({ filter: /terminal-history-legacy-scrollback-restore\.ts$/ }, () => ({ + contents: `${source}\nexport { truncateAltScreen }`, + loader: 'ts', + resolveDir: dirname(resolve(modulePath)) + })) + } + } + ] + }) + arms[name] = ( + await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) + ).truncateAltScreen +} + +const on = '\x1b[?1049h' +const off = '\x1b[?1049l' +let differentialCases = 0 +function verify(input) { + assert.equal(arms.indexed(input), arms.baseline(input)) + differentialCases++ +} +const tokens = [on, off, '\x1b[?1049', 'h', 'l', 'x'] +function enumerate(prefix, depth) { + verify(prefix) + if (depth === 0) { + return + } + for (const token of tokens) { + enumerate(prefix + token, depth - 1) + } +} +enumerate('', 6) + +let seed = 90211 +function random(max) { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return Math.floor((seed / 0x100000000) * max) +} +const fragments = [...tokens, '\r\n', '\x1b[?1047h', '\x1b[0m', 'é中😀', '\ud800', '\x00'] +for (let trial = 0; trial < 5000; trial++) { + verify(Array.from({ length: random(300) }, () => fragments[random(fragments.length)]).join('')) +} +console.log( + JSON.stringify({ + differentialCases, + node: process.version, + platform: process.platform, + arch: process.arch + }) +) + +const workloads = [ + ['empty', ''], + ['plain 4KiB', 'x'.repeat(4096)], + ['plain 16MiB', 'x'.repeat(16 * 1024 * 1024)], + ['8 balanced', `${'x'.repeat(256)}${on}TUI${off}`.repeat(8)], + ['1024 balanced', (on + 'x'.repeat(4096) + off + 'x'.repeat(4096)).repeat(1024)], + ['1024 off', ('x'.repeat(8192) + off).repeat(1024)], + ['1024 nested on', ('x'.repeat(8192) + on).repeat(1024)], + [ + '1024 nested closed', + ('x'.repeat(4096) + on).repeat(1024) + ('x'.repeat(4096) + off).repeat(1024) + ], + ['4096 off near 16MiB limit', ('x'.repeat(4088) + off).repeat(4096)] +] +function median(values) { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[3] + sorted[4]) / 2 +} +for (const [name, input] of workloads) { + const expected = arms.baseline(input) + assert.equal(arms.indexed(input), expected) + const samples = { baseline: [], indexed: [] } + const repeats = input.length < 8192 ? 10000 : 1 + for (const arm of Object.values(arms)) { + for (let warmup = 0; warmup < Math.min(100, repeats); warmup++) { + assert.equal(arm(input), expected) + } + } + for (const pair of buildCounterbalancedSchedule(8, 'baseline', 'indexed')) { + for (const name of pair) { + const start = performance.now() + let result + for (let repeat = 0; repeat < repeats; repeat++) { + result = arms[name](input) + } + samples[name].push((performance.now() - start) / repeats) + assert.equal(result, expected) + } + } + console.log( + JSON.stringify({ + name, + bytes: Buffer.byteLength(input), + medianMs: Object.fromEntries( + Object.entries(samples).map(([name, values]) => [name, median(values)]) + ) + }) + ) +} diff --git a/config/scripts/locale-brand-prefilter-benchmark.mjs b/config/scripts/locale-brand-prefilter-benchmark.mjs new file mode 100644 index 00000000000..c77e9f52ecd --- /dev/null +++ b/config/scripts/locale-brand-prefilter-benchmark.mjs @@ -0,0 +1,222 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import path from 'node:path' +import { performance } from 'node:perf_hooks' +import { pathToFileURL } from 'node:url' +import { build } from 'esbuild' + +// Pipe the baseline policy on stdin. Catalog repair mutates only fresh in-memory copies. +const policyPath = path.resolve('config/scripts/locale-translation-policy.mjs') +const verifierPath = path.resolve('config/scripts/verify-localization-catalog.mjs') +const sources = [readFileSync(0, 'utf8'), readFileSync(policyPath, 'utf8')] +assert(sources.every((source) => source.includes('function includesPreservedLatinTerm('))) +const modules = await Promise.all( + sources.map(async (source) => { + const result = await build({ + entryPoints: [verifierPath], + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: [ + { + name: 'actual-locale-policy', + setup(builder) { + builder.onResolve({ filter: /^\.\// }, (args) => { + const resolved = path.resolve(args.resolveDir, args.path) + return resolved === policyPath + ? { path: resolved } + : { path: pathToFileURL(resolved).href, external: true } + }) + builder.onResolve({ filter: /^typescript-api$/ }, () => ({ + path: import.meta.resolve('typescript-api'), + external: true + })) + builder.onLoad({ filter: /locale-translation-policy\.mjs$/ }, () => ({ + contents: `${source}\nexport { includesPreservedLatinTerm };`, + resolveDir: path.dirname(policyPath) + })) + builder.onLoad({ filter: /verify-localization-catalog\.mjs$/ }, () => ({ + contents: `${readFileSync(verifierPath, 'utf8')}\nexport * from './locale-translation-policy.mjs';`, + resolveDir: path.dirname(verifierPath) + })) + } + } + ] + }) + const code = `${result.outputFiles[0].text}\n//# sourceURL=locale-brand-prefilter-bundle.js` + return import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`) + }) +) + +const brands = [ + ...new Set(Object.values(modules[0].BRAND_MISTRANSLATIONS).flatMap(Object.keys)), + '', + '_', + 'a_b', + 'a.b', + '[term]', + '界', + '\ud800' +] +const boundaries = ['', ' ', 'X', '_', '2', '.', '-', '\n', '\0', 'é', '界', '😀', '\ud800'] +let comparisons = 0 +for (const term of brands) { + for (const prefix of boundaries) { + for (const suffix of boundaries) { + for (const value of [ + `${prefix}${term}${suffix}`, + `X${term}X ${prefix}${term}${suffix}`, + `${prefix}${term.toLowerCase()}${suffix}`, + `${prefix}${suffix}` + ]) { + assert.equal( + modules[1].includesPreservedLatinTerm(value, term), + modules[0].includesPreservedLatinTerm(value, term) + ) + comparisons += 1 + } + } + } +} +console.log(`${comparisons} literal/boundary differential cases match`) + +let repairCases = 0 +for (const [locale, translations] of Object.entries(modules[0].BRAND_MISTRANSLATIONS)) { + for (const [brand, wrongForms] of Object.entries(translations)) { + for (const wrong of wrongForms) { + for (const prefix of boundaries) { + for (const key of [ + 'fixture.brand', + 'fixture.search.brand', + 'auto.lib.agent.catalog.test' + ]) { + const input = { + key, + enValue: `${prefix}${brand}${prefix} fixture {{agent}}`, + localeValue: `${wrong} ${prefix}${brand}${prefix} ${wrong} {{agent}}`, + locale + } + assert.equal( + modules[1].repairTranslatedValue(input), + modules[0].repairTranslatedValue(input) + ) + repairCases += 1 + } + } + } + } +} +console.log(`${repairCases} full policy repair cases match`) + +function measured(run) { + const start = performance.now() + const value = run() + return { elapsed: performance.now() - start, value } +} + +function benchmark(name, prepare) { + const expected = prepare(modules[0])() + assert.deepEqual(prepare(modules[1])(), expected) + for (const module of modules) { + const until = performance.now() + 150 + do { + assert.deepEqual(prepare(module)(), expected) + } while (performance.now() < until) + } + /** @type {number[][]} */ + const times = [[], []] + for (let pair = 0; pair < 8; pair++) { + for (const index of pair % 2 ? [1, 0] : [0, 1]) { + const result = measured(prepare(modules[index])) + times[index].push(result.elapsed) + assert.deepEqual(result.value, expected) + } + } + const median = times.map((values) => { + const sorted = values.toSorted((a, b) => a - b) + return (sorted[3] + sorted[4]) / 2 + }) + console.log(JSON.stringify({ name, median, times })) +} + +console.log( + JSON.stringify({ + node: process.version, + platform: process.platform, + arch: process.arch, + unit: 'ms' + }) +) +const localesDir = path.resolve('src/renderer/src/i18n/locales') +const en = JSON.parse(readFileSync(path.join(localesDir, 'en.json'), 'utf8')) +const enEntries = new Map(modules[0].collectStringLeaves(en).map(({ key, value }) => [key, value])) +for (const locale of ['zh', 'ja', 'ko', 'es', 'fr']) { + const catalog = JSON.parse(readFileSync(path.join(localesDir, `${locale}.json`), 'utf8')) + const localeEntries = new Map( + modules[0].collectStringLeaves(catalog).map(({ key, value }) => [key, value]) + ) + const inputs = [...enEntries].flatMap(([key, enValue]) => { + const localeValue = localeEntries.get(key) + return typeof localeValue === 'string' ? [{ key, enValue, localeValue, locale }] : [] + }) + assert.deepEqual( + inputs.map(modules[1].repairTranslatedValue), + inputs.map(modules[0].repairTranslatedValue) + ) + const expressionCounts = modules.map((module) => { + const original = globalThis.RegExp + let count = 0 + globalThis.RegExp = new Proxy(original, { + construct(target, args) { + if (typeof args[0] === 'string' && args[0].startsWith('(^|[^A-Za-z_])')) { + count += 1 + } + return Reflect.construct(target, args) + } + }) + try { + inputs.forEach(module.repairTranslatedValue) + } finally { + globalThis.RegExp = original + } + return count + }) + console.log(JSON.stringify({ locale, leaves: inputs.length, expressionCounts })) + benchmark(`${locale}: repairCatalog`, (module) => { + const copy = structuredClone(catalog) + return () => ({ count: module.repairCatalog(en, copy, locale), catalog: copy }) + }) + benchmark(`${locale}: collectGenericTermRegressions`, (module) => { + return () => module.collectGenericTermRegressions(enEntries, localeEntries, locale) + }) +} + +for (const [name, enValue, localeValue] of [ + ['absent brands', 'Choose an endpoint.', 'Elegir un destino.'], + ['matching brand', 'Use Gemini.', 'Usar Géminis.'], + ['embedded only', 'Use _Gemini_.', 'Usar Géminis.'], + ['all brands', brands.join(' '), brands.join(' ')] +]) { + const input = { key: 'fixture.brand', enValue, localeValue, locale: 'es' } + benchmark(`10k strings: ${name}`, (module) => () => { + let result + for (let i = 0; i < 10000; i++) { + result = module.repairTranslatedValue(input) + } + return result + }) +} + +const cache = new Map([ + ['Use Gemini.', 'Usar Géminis.'], + ['Choose an endpoint.', 'Elegir un destino.'], + ['Use _Gemini_.', 'Usar Géminis.'], + ['Use GitHub Copilot.', 'Usar Copiloto de GitHub.'] +]) +const caches = modules.map((module) => { + const copy = new Map(cache) + return { count: module.repairCacheMap(copy, 'es'), entries: [...copy] } +}) +assert.deepEqual(caches[1], caches[0]) +console.log('Actual catalog outputs, regression reports, repair counts and cache mutation match') diff --git a/config/scripts/locale-brand-prefilter.test.mjs b/config/scripts/locale-brand-prefilter.test.mjs new file mode 100644 index 00000000000..0ea14ffea3b --- /dev/null +++ b/config/scripts/locale-brand-prefilter.test.mjs @@ -0,0 +1,55 @@ +import { describe, expect, it, vi } from 'vitest' +import { repairTranslatedValue } from './locale-translation-policy.mjs' + +describe('locale brand matching', () => { + it('does not construct boundary expressions for absent brands', () => { + let boundaryExpressions = 0 + vi.stubGlobal( + 'RegExp', + new Proxy(RegExp, { + construct(target, args) { + if (typeof args[0] === 'string' && args[0].startsWith('(^|[^A-Za-z_])')) { + boundaryExpressions += 1 + } + return Reflect.construct(target, args) + } + }) + ) + try { + for (const locale of ['zh', 'ja', 'ko', 'es']) { + expect( + repairTranslatedValue({ + key: 'fixture.endpoint', + enValue: 'Choose an endpoint.', + localeValue: 'fixture translation', + locale + }) + ).toBe('fixture translation') + } + } finally { + vi.unstubAllGlobals() + } + expect(boundaryExpressions).toBe(0) + }) + + it.each([ + ['Use Gemini.', 'Usar Géminis.', 'Usar Gemini.'], + ['Use GeminiX.', 'Usar Géminis.', 'Usar Géminis.'], + ['Use XGemini.', 'Usar Géminis.', 'Usar Géminis.'], + ['Use _Gemini_.', 'Usar Géminis.', 'Usar Géminis.'], + ['Use Gemini_2.', 'Usar Géminis.', 'Usar Géminis.'], + ['Use 2Gemini3.', 'Usar Géminis.', 'Usar Gemini.'], + ['Use (Gemini).', 'Usar Géminis.', 'Usar Gemini.'], + ['Use éGemini界.', 'Usar Géminis.', 'Usar Gemini.'], + ['Use gemini.', 'Usar Géminis.', 'Usar Géminis.'], + ['Use GeminiX and Gemini.', 'Usar Géminis.', 'Usar Gemini.'], + ['Use Gemini.', 'Gemini y Géminis.', 'Gemini y Géminis.'], + ['Use Gemini.', '_Gemini_ y Géminis.', '_Gemini_ y Gemini.'], + ['Use GitHub Copilot.', 'Usar Copiloto de GitHub.', 'Usar GitHub Copilot.'], + ['Use XGitHub CopilotY.', 'Usar Copiloto de GitHub.', 'Usar GitHub Copilot.'] + ])('preserves literal and boundary matching for %j / %j', (enValue, localeValue, expected) => { + expect( + repairTranslatedValue({ key: 'fixture.brand', enValue, localeValue, locale: 'es' }) + ).toBe(expected) + }) +}) diff --git a/config/scripts/locale-translation-policy.mjs b/config/scripts/locale-translation-policy.mjs index cec2ebf63ad..af88805b8c4 100644 --- a/config/scripts/locale-translation-policy.mjs +++ b/config/scripts/locale-translation-policy.mjs @@ -281,8 +281,11 @@ function escapeRegExp(value) { } function includesPreservedLatinTerm(value, term) { + if (!value.includes(term)) { + return false + } if (!/^[A-Za-z_]+$/.test(term)) { - return value.includes(term) + return true } return new RegExp(`(^|[^A-Za-z_])${escapeRegExp(term)}($|[^A-Za-z_])`).test(value) } diff --git a/config/scripts/markdown-blank-run-scan-benchmark.mjs b/config/scripts/markdown-blank-run-scan-benchmark.mjs new file mode 100644 index 00000000000..2caaef90851 --- /dev/null +++ b/config/scripts/markdown-blank-run-scan-benchmark.mjs @@ -0,0 +1,94 @@ +#!/usr/bin/env node +// Adverse-input audit: compares the previous scanner with the production line-bounded implementation. +import assert from 'node:assert/strict' +import { readFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' +import { summarizeBenchmarkSamples } from './benchmark-sample-summary.mjs' + +const entry = fileURLToPath( + new URL( + '../../src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.ts', + import.meta.url + ) +) +const source = await readFile(entry, 'utf8') +const current = `${String.raw`/\s*(?:`}\`\`\`|~~~)/y` +const replacement = `${String.raw`/[^\S\n]*(?:`}\`\`\`|~~~)/y` +assert.ok( + source.includes(replacement), + 'Production fence regex changed; re-review benchmark candidate' +) +async function load(candidate) { + const result = await build({ + entryPoints: [entry], + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: candidate + ? [ + { + name: 'line-bounded-candidate', + setup(plugin) { + plugin.onLoad({ filter: /monaco-markdown-doc-link-decorations\.ts$/ }, () => ({ + contents: source.replace(replacement, current), + loader: 'ts', + resolveDir: fileURLToPath( + new URL('../../src/renderer/src/components/editor/', import.meta.url) + ) + })) + } + } + ] + : [] + }) + return ( + await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) + ).getMarkdownDocLinkDecorationRanges +} +const baseline = await load(true) +const candidate = await load(false) +const corpus = [ + '', + '\n```\n[[hidden.md]]\n```\n[[shown.md]]', + ' \t\r\n~~~\r\n[[hidden.md]]\r\n~~~\r\n[[shown.md]]', + '\u00a0\u2028```\n[[hidden.md]]\n```\n[[shown.md]]', + '`code` [[shown.md]]' +] +for (const content of corpus) { + assert.deepEqual(candidate(content), baseline(content)) +} +const scenarios = [ + ['ordinary-100k-lines', 'ordinary prose\n'.repeat(100_000)], + ['blank-10k-lines', '\n'.repeat(10_000)], + ['blank-30k-lines', '\n'.repeat(30_000)], + ['blank-100k-lines', '\n'.repeat(100_000)], + ['indented-blank-10k-lines', `${' '.repeat(80)}\n`.repeat(10_000)] +] +for (const [name, content] of scenarios) { + const samples = { baseline: [], candidate: [] } + const scanners = { baseline, candidate } + let expected + for (const arms of buildCounterbalancedSchedule(2, 'baseline', 'candidate')) { + for (const arm of arms) { + const started = performance.now() + const ranges = scanners[arm](content) + samples[arm].push(performance.now() - started) + expected ??= ranges + assert.deepEqual(ranges, expected) + } + } + console.log( + JSON.stringify({ + name, + bytes: Buffer.byteLength(content), + samples, + baseline: summarizeBenchmarkSamples(samples.baseline), + candidate: summarizeBenchmarkSamples(samples.candidate) + }) + ) +} diff --git a/config/scripts/markdown-note-line-scan-benchmark.mjs b/config/scripts/markdown-note-line-scan-benchmark.mjs new file mode 100644 index 00000000000..2b6565278d9 --- /dev/null +++ b/config/scripts/markdown-note-line-scan-benchmark.mjs @@ -0,0 +1,81 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const sourcePath = 'src/renderer/src/lib/markdown-review-notes.ts' +const baseline = process.argv[2] ?? '20ab9950654' +async function load(contents) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(sourcePath)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false + }) + return import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) +} +const before = await load( + execFileSync('git', ['show', `${baseline}:${sourcePath}`], { encoding: 'utf8' }) +) +const after = await load(readFileSync(sourcePath, 'utf8')) +const results = [] +for (const [name, lineCount, width, count, iterations] of [ + ['small', 20, 40, 1, 1000], + ['long-lines', 1000, 1000, 20, 3], + ['many-lines', 20000, 80, 20, 3], + ['early-note', 20000, 80, 1, 1000] +]) { + const content = Array.from({ length: lineCount }, (_, i) => `${i}: ${'x'.repeat(width)}`).join( + '\r\n' + ) + const notes = Array.from({ length: count }, (_, i) => ({ + id: `${i}`, + worktreeId: 'bench', + filePath: 'README.md', + source: 'markdown', + lineNumber: name === 'early-note' ? 2 : lineCount - i, + body: 'Clarify this line', + createdAt: i, + side: 'modified' + })) + assert.equal( + after.formatMarkdownReviewNotes(notes, content), + before.formatMarkdownReviewNotes(notes, content) + ) + const arms = { before, after } + const samples = { before: [], after: [] } + function run(arm) { + const start = performance.now() + for (let i = 0; i < iterations; i++) { + arms[arm].formatMarkdownReviewNotes(notes, content) + } + return (performance.now() - start) / iterations + } + for (let i = 0; i < 6; i++) { + run('before') + run('after') + } + for (const pair of buildCounterbalancedSchedule(12, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + const median = (xs) => xs.sort((a, b) => a - b)[Math.floor(xs.length / 2)] + results.push({ + name, + lineCount, + width, + count, + beforeMs: median(samples.before), + afterMs: median(samples.after) + }) +} +console.log( + JSON.stringify({ node: process.version, platform: process.platform, baseline, results }, null, 2) +) diff --git a/config/scripts/mobile-backspace-benchmark.mjs b/config/scripts/mobile-backspace-benchmark.mjs new file mode 100644 index 00000000000..f1839089776 --- /dev/null +++ b/config/scripts/mobile-backspace-benchmark.mjs @@ -0,0 +1,112 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' +import { summarizeBenchmarkSamples } from './benchmark-sample-summary.mjs' + +// git show :mobile/src/terminal/terminal-live-text-commit.ts | node config/scripts/mobile-backspace-benchmark.mjs +const target = resolve('mobile/src/terminal/terminal-live-text-commit.ts') +async function load(source) { + const result = await build({ + stdin: { contents: source, loader: 'ts', resolveDir: dirname(target) }, + bundle: true, + write: false, + platform: 'node', + format: 'esm' + }) + return ( + await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) + ).getTerminalLiveAccessoryLocalEditText +} +const baseline = readFileSync(0, 'utf8') +assert.ok( + baseline.includes('function getTerminalLiveAccessoryLocalEditText'), + 'Pipe baseline source into stdin' +) +const implementations = { + before: await load(baseline), + after: await load(readFileSync(target, 'utf8')) +} +const tokens = [ + '', + 'a', + '\u0000', + '\r', + '\n', + '한', + '\u0301', + '\u200d', + '🙂', + '\ud800', + '\udbff', + '\udc00', + '\udfff' +] +let cases = 0 +for (const first of tokens) { + for (const second of tokens) { + for (const third of tokens) { + for (const localEdit of ['backspace', 'delete']) { + const input = { fieldText: first + second + third, localEdit } + assert.equal( + implementations.after(input), + implementations.before(input), + JSON.stringify(input) + ) + cases += 1 + } + } + } +} +const results = [] +for (const inputBytes of [32, 4096, 65_536, 262_144]) { + for (const glyph of ['a', '🙂']) { + const fieldText = glyph.repeat(inputBytes / Buffer.byteLength(glyph)) + const input = { localEdit: 'backspace', fieldText } + const expected = implementations.before(input) + assert.equal(implementations.after(input), expected) + const iterations = Math.max(10, Math.floor(1_000_000 / inputBytes)) + for (let warmup = 0; warmup < 100; warmup += 1) { + implementations.before(input) + implementations.after(input) + } + /** @type {{ before: number[], after: number[] }} */ + const samples = { before: [], after: [] } + for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) { + for (const arm of pair) { + let actual + const started = performance.now() + for (let repeat = 0; repeat < iterations; repeat += 1) { + actual = implementations[arm](input) + } + samples[arm].push(performance.now() - started) + assert.equal(actual, expected) + } + } + const means = Object.fromEntries( + Object.entries(samples).map(([arm, values]) => [ + arm, + (values.reduce((sum, ms) => sum + ms, 0) * 1000) / values.length / iterations + ]) + ) + results.push({ + inputBytes, + glyph, + iterations, + meanMicrosecondsPerCall: means, + before: summarizeBenchmarkSamples(samples.before), + after: summarizeBenchmarkSamples(samples.after) + }) + } +} +console.log( + JSON.stringify( + { node: process.version, platform: process.platform, differentialCases: cases, results }, + null, + 2 + ) +) diff --git a/config/scripts/mobile-diagnostics-prefix-benchmark.mjs b/config/scripts/mobile-diagnostics-prefix-benchmark.mjs new file mode 100644 index 00000000000..c7beec3eba1 --- /dev/null +++ b/config/scripts/mobile-diagnostics-prefix-benchmark.mjs @@ -0,0 +1,236 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import path from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' + +// Pipe baseline report then submission modules on stdin, in that order. No device/network I/O. +const baseline = readFileSync(0, 'utf8') +const divider = '\nconst CONNECTION_DIAGNOSTICS_ENDPOINT = ' +assert.equal(baseline.split(divider).length, 2) +const split = baseline.indexOf(divider) + 1 +const files = ['report', 'submission'].map((name) => + path.resolve(`mobile/src/diagnostics/connection-diagnostics-${name}.ts`) +) +const sources = [ + [baseline.slice(0, split), baseline.slice(split)], + files.map((file) => readFileSync(file, 'utf8')) +] +const modules = await Promise.all( + sources.map(async (contents) => { + const result = await build({ + stdin: { + contents: files.map((file) => `export * from ${JSON.stringify(file)};`).join('\n'), + resolveDir: process.cwd() + }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: [ + { + name: 'actual-mobile-diagnostics', + setup(builder) { + builder.onLoad( + { filter: /connection-diagnostics-(report|submission)\.ts$/ }, + (args) => ({ + contents: contents[files.indexOf(args.path)], + loader: 'ts', + resolveDir: path.dirname(args.path) + }) + ) + builder.onResolve({ filter: /^@react-native-async-storage\/async-storage$/ }, () => ({ + path: 'forbidden-device-storage', + namespace: 'fixture' + })) + builder.onLoad({ filter: /.*/, namespace: 'fixture' }, () => ({ + contents: `function forbidden() { throw new Error('Device storage is forbidden'); } + export default { getItem: forbidden, setItem: forbidden };` + })) + } + } + ] + }) + const code = `${result.outputFiles[0].text}\n//# sourceURL=mobile-diagnostics-prefix-bundle.js` + return import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`) + }) +) + +const base = { + hostName: 'fixture', + endpoint: 'ws://192.168.1.2:6768', + state: 'reconnecting', + reconnectAttempts: 2, + lastConnectedAt: null, + platform: 'android', + appVersion: 'fixture', + nowMs: 1700000000000 +} +let seed = 0x20d1a6 +function random(max) { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return (seed >>> 8) % max +} +const tokens = ['a', 'é', '界', '😀', '\ud800', '\udc00', '\n', '\r\n', '\0', 'e\u0301'] +const limits = [ + Number.NEGATIVE_INFINITY, + -1, + 0, + 1, + 2, + 3, + 4, + 15, + 16, + 17, + 100, + 511, + 2048, + 65536, + Number.POSITIVE_INFINITY, + Number.NaN, + 2.5 +] +for (let trace = 0; trace < 3000; trace++) { + const lines = Array.from({ length: 1 + random(12) }, () => + Array.from({ length: 1 + random(5) }, () => + tokens[random(tokens.length)].repeat(random(100)) + ).join('') + ) + if (trace % 2) { + lines.splice(random(lines.length), 0, 'Recent connection history (fixture):') + } + const report = lines.join('\n') + const limit = limits[random(limits.length)] + assert.equal( + modules[1].boundConnectionDiagnosticsReport(report, limit), + modules[0].boundConnectionDiagnosticsReport(report, limit) + ) +} +console.log('3,000 report-bound differentials match, including nonfinite/fractional limits') + +for (let trace = 0; trace < 600; trace++) { + const entries = Object.freeze( + Array.from({ length: random(12) }, (_, index) => + Object.freeze({ + id: String(index), + ts: base.nowMs + index, + level: ['info', 'error', 'warn'][random(3)], + message: ['Authenticated', 'relay director resolve failed (503)', 'fixture'][random(3)], + detail: `${tokens[random(tokens.length)].repeat(random(4000))} token=fixture-secret`, + code: ['client-session-started', 'liveness-timeout', undefined][random(3)], + path: ['relay', 'lan', 'tailscale'][random(3)] + }) + ) + ) + const args = Object.freeze({ + ...base, + hostName: 'fixture token=host-fixture-secret', + endpoint: trace % 2 ? base.endpoint : 'invalid?token=endpoint-fixture-secret', + desktopAppVersion: trace % 2 ? '1.2.3' : '\ninvalid', + state: ['connected', 'reconnecting', 'connecting'][random(3)], + activePath: ['relay', 'lan', 'tailscale'][random(3)], + pendingPath: trace % 3 ? null : 'relay', + entries + }) + const reports = modules.map((module) => module.buildConnectionDiagnosticsReport(args)) + assert.equal(reports[1], reports[0]) + assert(!reports[1].includes('fixture-secret')) + const limit = limits[random(limits.length)] + assert.equal( + modules[1].boundConnectionDiagnosticsReport(reports[1], limit), + modules[0].boundConnectionDiagnosticsReport(reports[0], limit) + ) +} +console.log('600 frozen report-build + bound journeys preserve redaction, diagnosis and exact text') + +function runSample(run, repeats) { + let value + const start = performance.now() + for (let index = 0; index < repeats; index++) { + value = run() + } + return { value, elapsed: (performance.now() - start) / repeats } +} +function benchmark(name, arms) { + const expected = arms[0]() + assert.equal(arms[1](), expected) + for (const arm of arms) { + const until = performance.now() + 200 + do { + assert.equal(arm(), expected) + } while (performance.now() < until) + } + const repeats = Math.max(3, Math.min(10000, Math.ceil(40 / runSample(arms[0], 1).elapsed))) + /** @type {number[][]} */ + const times = [[], []] + for (let pair = 0; pair < 8; pair++) { + for (const index of pair % 2 ? [1, 0] : [0, 1]) { + const result = runSample(arms[index], repeats) + assert.equal(result.value, expected) + times[index].push(result.elapsed) + } + } + const median = times.map((values) => { + const sorted = values.toSorted((a, b) => a - b) + return (sorted[3] + sorted[4]) / 2 + }) + console.log(JSON.stringify({ name, repeats, median, times })) +} +console.log( + JSON.stringify({ + node: process.version, + platform: process.platform, + arch: process.arch, + unit: 'ms' + }) +) +for (const [events, length, token] of [ + [0, 0, 'a'], + [20, 80, 'a'], + [200, 80, 'a'], + [200, 1000, 'a'], + [200, 4000, 'a'], + [200, 2000, '😀'] +]) { + const args = { + ...base, + entries: Array.from({ length: events }, (_, i) => ({ + id: String(i), + ts: base.nowMs + i, + level: 'error', + message: `fixture-${i} ${token.repeat(length)}` + })) + } + const reports = modules.map((module) => module.buildConnectionDiagnosticsReport(args)) + assert.equal(reports[1], reports[0]) + const label = `${events} events / ${length} ${token}` + benchmark( + `${label}: build`, + modules.map((module) => () => module.buildConnectionDiagnosticsReport(args)) + ) + benchmark( + `${label}: bound`, + modules.map((module) => () => module.boundConnectionDiagnosticsReport(reports[0])) + ) +} + +for (const token of ['a', '😀', '\ud800']) { + const report = token.repeat(100000) + const results = await Promise.all( + modules.map(async (module) => { + let request + const result = await module.submitConnectionDiagnostics( + { report, platform: 'android', appVersion: 'fixture' }, + async (url, options) => { + assert.equal(options.signal.aborted, false) + request = { url, method: options.method, headers: options.headers, body: options.body } + return { ok: true } + } + ) + return { result, request } + }) + ) + assert.deepEqual(results[1], results[0]) +} +console.log('Three fake-fetch submission journeys preserve complete request bytes and results') diff --git a/config/scripts/mobile-file-ranking-benchmark.mjs b/config/scripts/mobile-file-ranking-benchmark.mjs index 68ac5b9d977..27bf99f8ecd 100644 --- a/config/scripts/mobile-file-ranking-benchmark.mjs +++ b/config/scripts/mobile-file-ranking-benchmark.mjs @@ -1,53 +1,141 @@ import assert from 'node:assert/strict' import { execFileSync } from 'node:child_process' import { readFileSync } from 'node:fs' -import { stripTypeScriptTypes } from 'node:module' import { performance } from 'node:perf_hooks' +import { transform } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' +import { summarizeBenchmarkSamples } from './benchmark-sample-summary.mjs' const baseline = process.argv[2] if (!baseline) { - throw new Error('Usage: node config/scripts/mobile-file-ranking-benchmark.mjs ') + throw new Error( + 'Usage: node config/scripts/mobile-file-ranking-benchmark.mjs ' + ) } +// git show :mobile/src/session/mobile-native-chat-autocomplete.ts | node config/scripts/mobile-file-ranking-benchmark.mjs --autocomplete-stdin +const autocompleteSource = baseline === '--autocomplete-stdin' ? readFileSync(0, 'utf8') : null async function load(source) { - const js = stripTypeScriptTypes(source, { mode: 'transform' }) - return await import(`data:text/javascript;base64,${Buffer.from(js).toString('base64')}`) -} -function measure(fn, paths, query) { - for (let warmup = 0; warmup < 10; warmup++) { - fn(paths, query, 16) - } - const samples = [] - for (let i = 0; i < 9; i++) { - const start = performance.now() - fn(paths, query, 16) - samples.push(performance.now() - start) - } - return samples.sort((a, b) => a - b)[4] + const { code } = await transform(source, { loader: 'ts', format: 'esm' }) + return await import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`) } const results = [] +let differentialCases = 0 for (const [file, name] of [ ['src/main/runtime/runtime-mobile-file-path-search.ts', 'rankRuntimeMobileFilePaths'], - ['mobile/src/session/mobile-native-chat-autocomplete.ts', 'rankSuggestions'] + ['mobile/src/session/mobile-native-chat-autocomplete.ts', 'rankSuggestions'], + ['mobile/src/session/mobile-native-chat-autocomplete.ts', 'rankSlashCommandSuggestions'] ]) { + if (autocompleteSource !== null && name === 'rankRuntimeMobileFilePaths') { + continue + } const before = ( - await load(execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' })) + await load( + autocompleteSource ?? + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }) + ) )[name] const after = (await load(readFileSync(file, 'utf8')))[name] - for (const count of [100, 100000]) { - const paths = Array.from( - { length: count }, - (_, i) => `src/components/workspace/group-${i % 100}/file-${i}.tsx` + const slash = name === 'rankSlashCommandSuggestions' + const toCandidates = (names) => + slash ? names.map((name, index) => ({ name, description: `Command ${index}` })) : names + if (name !== 'rankRuntimeMobileFilePaths') { + let seed = 42 + const random = (max) => { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return seed % max + } + const tokens = ['', 'app', 'src/', 'APP', 'zapp', '🙂', '한', '\ud800', '\u0130', ' '] + const limits = [ + undefined, + 0, + -0, + -1, + -0.5, + -Infinity, + Number.NaN, + 0.5, + 1.5, + 2.5, + 8, + 16, + Infinity + ] + for (let index = 0; index < 3000; index += 1) { + const candidates = toCandidates( + Array.from( + { length: random(100) }, + () => tokens[random(tokens.length)] + tokens[random(tokens.length)] + ) + ) + const query = tokens[random(tokens.length)] + const limit = limits[random(limits.length)] + assert.deepEqual(after(candidates, query, limit), before(candidates, query, limit)) + differentialCases += 1 + } + } + for (const count of slash ? [16, 100, 1000] : [16, 100, 10_000, 50_000, 100_000]) { + const names = Array.from({ length: count }, (_, index) => + slash + ? `team-review-${index}` + : `src/components/workspace/group-${index % 100}/file-${index}.tsx` ) - for (const query of ['file-9', 'missing', 'workspace']) { - assert.deepEqual(after(paths, query, 16), before(paths, query, 16)) + const limit = slash ? 12 : 16 + const substringQuery = slash ? 'review' : 'workspace' + const workloads = [ + { name: 'empty-query', names, query: '' }, + { name: 'substring', names, query: substringQuery }, + { name: 'no-match', names, query: 'missing' }, + { name: 'early-prefix', names, query: slash ? 'team' : 'file' }, + { + name: 'late-prefix', + names: [...names, ...Array.from({ length: 4 }, (_, index) => `${substringQuery}-${index}`)], + query: substringQuery + } + ] + for (const workload of workloads) { + const candidates = toCandidates(workload.names) + const expected = before(candidates, workload.query, limit) + assert.deepEqual(after(candidates, workload.query, limit), expected) + const implementations = { before, after } + const iterations = Math.max(10, Math.floor(100_000 / count)) + for (let warmup = 0; warmup < 100; warmup += 1) { + before(candidates, workload.query, limit) + after(candidates, workload.query, limit) + } + /** @type {{ before: number[], after: number[] }} */ + const samples = { before: [], after: [] } + for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) { + for (const arm of pair) { + let actual + const start = performance.now() + for (let repeat = 0; repeat < iterations; repeat += 1) { + actual = implementations[arm](candidates, workload.query, limit) + } + samples[arm].push(performance.now() - start) + assert.deepEqual(actual, expected) + } + } results.push({ function: name, - paths: count, - query, - beforeMs: measure(before, paths, query), - afterMs: measure(after, paths, query) + candidates: candidates.length, + workload: workload.name, + iterations, + meanMicrosecondsPerCall: Object.fromEntries( + Object.entries(samples).map(([arm, values]) => [ + arm, + (values.reduce((sum, ms) => sum + ms, 0) * 1000) / values.length / iterations + ]) + ), + before: summarizeBenchmarkSamples(samples.before), + after: summarizeBenchmarkSamples(samples.after) }) } } } -console.log(JSON.stringify({ node: process.version, platform: process.platform, results }, null, 2)) +console.log( + JSON.stringify( + { node: process.version, platform: process.platform, differentialCases, results }, + null, + 2 + ) +) diff --git a/config/scripts/mobile-history-scope-paths-benchmark.mjs b/config/scripts/mobile-history-scope-paths-benchmark.mjs new file mode 100644 index 00000000000..dda318223f2 --- /dev/null +++ b/config/scripts/mobile-history-scope-paths-benchmark.mjs @@ -0,0 +1,91 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] +if (!baseline) { + throw new Error( + 'Usage: node config/scripts/mobile-history-scope-paths-benchmark.mjs ' + ) +} +const file = 'mobile/src/agent-history/agent-history-scope-paths.ts' +async function load(contents) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + logLevel: 'silent', + tsconfigRaw: {} + }) + return ( + await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) + ).deriveMobileAiVaultScopePaths +} +const arms = { + before: await load(execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' })), + after: await load(readFileSync(file, 'utf8')) +} +const iterations = 200 +const results = [] +for (const [count, unique] of [ + [1, 1], + [16, 16], + [64, 64], + [1000, 32] +]) { + for (const root of ['/home/ada/café/project', 'C:\\Users\\ada\\café\\project']) { + const rows = Array.from({ length: count }, (_, index) => ({ + worktreeId: `w-${index}`, + repoId: 'repo', + path: `${root}/workspace-${index % unique}` + })) + const expected = arms.before('project', rows[0], rows) + assert.deepEqual(arms.after('project', rows[0], rows), expected) + const samples = { before: [], after: [] } + function run(arm) { + let length = 0 + const start = performance.now() + for (let i = 0; i < iterations; i++) { + length += arms[arm]('project', rows[0], rows).length + } + const elapsed = performance.now() - start + assert.equal(length, iterations * expected.length) + return elapsed / iterations + } + for (const arm of ['before', 'after']) { + run(arm) + } + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + const median = (values) => { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + results.push({ + count, + unique, + root, + beforeMs: median(samples.before), + afterMs: median(samples.after), + samples + }) + } +} +console.log( + JSON.stringify( + { baseline, node: process.version, platform: process.platform, iterations, results }, + null, + 2 + ) +) diff --git a/config/scripts/mobile-linear-sort-benchmark.mjs b/config/scripts/mobile-linear-sort-benchmark.mjs new file mode 100644 index 00000000000..1a2a2d29ad6 --- /dev/null +++ b/config/scripts/mobile-linear-sort-benchmark.mjs @@ -0,0 +1,104 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] +if (!baseline) { + throw new Error('Usage: node config/scripts/mobile-linear-sort-benchmark.mjs ') +} +async function load(file, contents) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + logLevel: 'silent', + tsconfigRaw: {}, + plugins: [ + { + name: 'theme-only', + setup(bundler) { + bundler.onResolve({ filter: /mobile-tasks-dependencies$/ }, () => ({ + path: resolve('mobile/src/theme/mobile-theme.ts') + })) + } + } + ] + }) + return await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) +} +const file = 'mobile/src/tasks/mobile-tasks-reviewer-linear.ts' +const before = await load( + file, + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }) +) +const after = await load(file, readFileSync(file, 'utf8')) +const results = [] +for (const count of [0, 1, 25, 200, 1000]) { + const items = Array.from({ length: count }, (_, index) => ({ + id: `item-${index}`, + identifier: `ENG-${(index * 37) % Math.max(1, count)}`, + updatedAt: new Date(1700000000000 - index * 100000).toISOString(), + priority: index % 5 + })) + for (const sort of ['updated', 'identifier', 'priority']) { + const arms = { + before: () => [...items].sort((a, b) => before.compareLinearIssues(a, b, sort)), + after: () => after.sortLinearIssues(items, sort) + } + assert.deepEqual(arms.after(), arms.before()) + const iterations = count < 100 ? 100 : 10 + function run(arm) { + const start = performance.now() + for (let i = 0; i < iterations; i++) { + arms[arm]() + } + return (performance.now() - start) / iterations + } + const samples = { before: [], after: [] } + run('before') + run('after') + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + function median(values) { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + const dateParses = {} + const nativeParse = Date.parse + for (const arm of ['before', 'after']) { + let calls = 0 + Date.parse = (value) => { + calls++ + return nativeParse(value) + } + try { + arms[arm]() + } finally { + Date.parse = nativeParse + } + dateParses[arm] = calls + } + results.push({ + count, + sort, + dateParses, + beforeMs: median(samples.before), + afterMs: median(samples.after), + samples + }) + } +} +console.log( + JSON.stringify({ baseline, node: process.version, platform: process.platform, results }, null, 2) +) diff --git a/config/scripts/mobile-log-revisions-benchmark.mjs b/config/scripts/mobile-log-revisions-benchmark.mjs new file mode 100644 index 00000000000..aa42094fee6 --- /dev/null +++ b/config/scripts/mobile-log-revisions-benchmark.mjs @@ -0,0 +1,85 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] ?? '20ab9950654' +const file = 'mobile/src/transport/connection-log-buffer.ts' +async function load(contents) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + tsconfigRaw: {} + }) + return import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) +} +const before = await load( + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }) +) +const after = await load(readFileSync(file, 'utf8')) +const drain = () => new Promise((resolve) => setImmediate(resolve)) +async function run(module, count, startup) { + let calls = 0 + let bytes = 0 + let stored = '' + const store = module.createConnectionLogStore(200, { + load: async () => [], + save: async (_host, snapshot) => { + stored = JSON.stringify(snapshot) + calls++ + bytes += Buffer.byteLength(stored) + } + }) + if (!startup) { + await store.hydrate('a') + await drain() + calls = 0 + bytes = 0 + } + const start = performance.now() + for (let i = 0; i < count; i++) { + store.append('a', { id: `${i}`, ts: i, level: 'info', message: `connection event ${i}` }) + } + await drain() + return { ms: performance.now() - start, calls, bytes, stored } +} +const results = [] +for (const count of [1, 25, 200, 1000]) { + for (const startup of [false, true]) { + const arms = { before, after } + const initialBefore = await run(before, count, startup) + const initialAfter = await run(after, count, startup) + assert.equal(initialAfter.stored, initialBefore.stored) + const samples = { before: [], after: [] } + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push((await run(arms[arm], count, startup)).ms) + } + } + const median = (values) => { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + results.push({ + count, + startup, + before: { + calls: initialBefore.calls, + bytes: initialBefore.bytes, + ms: median(samples.before) + }, + after: { calls: initialAfter.calls, bytes: initialAfter.bytes, ms: median(samples.after) } + }) + } +} +console.log( + JSON.stringify({ baseline, node: process.version, platform: process.platform, results }, null, 2) +) diff --git a/config/scripts/mobile-source-control-collation-benchmark.mjs b/config/scripts/mobile-source-control-collation-benchmark.mjs new file mode 100644 index 00000000000..e48d7dbfb0f --- /dev/null +++ b/config/scripts/mobile-source-control-collation-benchmark.mjs @@ -0,0 +1,110 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] +if (!baseline) { + throw new Error( + 'Usage: node config/scripts/mobile-source-control-collation-benchmark.mjs ' + ) +} +async function load(file, contents, name) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + logLevel: 'silent', + tsconfigRaw: {} + }) + return ( + await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) + )[name] +} +// Match git's path order, including its numeric-looking names, instead of inflating sort work with a shuffle. +const paths = execFileSync('git', ['ls-files', '-z'], { maxBuffer: 16 * 1024 * 1024 }) + .toString() + .split('\0') + .filter(Boolean) +const results = [] +for (const [file, name] of [ + ['mobile/src/source-control/mobile-git-status.ts', 'buildMobileSourceControlSections'], + ['mobile/src/source-control/mobile-branch-compare.ts', 'buildMobileBranchCompareSection'], + ['mobile/src/session/mobile-diff-review-queue.ts', 'buildMobileDiffReviewQueue'] +]) { + const arms = { + before: await load( + file, + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }), + name + ), + after: await load(file, readFileSync(file, 'utf8'), name) + } + for (const count of [0, 1, 17, 63, 1000]) { + const step = Math.max(1, Math.floor(paths.length / Math.max(1, count))) + const entries = Array.from({ length: count }, (_, index) => ({ + path: paths[index * step], + area: 'unstaged', + status: 'modified', + ...(index % 37 === 0 ? { conflictStatus: 'unresolved' } : {}) + })) + const input = + name === 'buildMobileDiffReviewQueue' + ? { + worktreeId: 'workspace', + statusEntries: entries, + branchEntries: [], + comments: [], + reviewState: { version: 1, files: {} } + } + : entries + assert.deepEqual(arms.after(input), arms.before(input)) + const iterations = count < 100 ? 100 : 10 + function run(arm) { + const start = performance.now() + for (let index = 0; index < iterations; index++) { + arms[arm](input) + } + return (performance.now() - start) / iterations + } + const samples = { before: [], after: [] } + run('before') + run('after') + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + function median(values) { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + results.push({ + name, + count, + beforeMs: median(samples.before), + afterMs: median(samples.after), + samples + }) + } +} +console.log( + JSON.stringify( + { + baseline, + node: process.version, + platform: process.platform, + locale: new Intl.Collator().resolvedOptions().locale, + results + }, + null, + 2 + ) +) diff --git a/config/scripts/mobile-task-sort-benchmark.mjs b/config/scripts/mobile-task-sort-benchmark.mjs new file mode 100644 index 00000000000..b7d6cb469a3 --- /dev/null +++ b/config/scripts/mobile-task-sort-benchmark.mjs @@ -0,0 +1,113 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] +if (!baseline) { + throw new Error('Usage: node config/scripts/mobile-task-sort-benchmark.mjs ') +} +async function load(file, contents) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + logLevel: 'silent', + tsconfigRaw: {}, + plugins: [ + { + name: 'theme-only', + setup(bundler) { + bundler.onResolve({ filter: /mobile-tasks-dependencies$/ }, () => ({ + path: resolve('mobile/src/theme/mobile-theme.ts') + })) + } + } + ] + }) + return await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) +} +const file = 'mobile/src/tasks/mobile-tasks-repository-presentation.ts' +const before = await load( + file, + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }) +) +const after = await load(file, readFileSync(file, 'utf8')) +const repos = new Map() +const results = [] +for (const count of [0, 1, 25, 1000]) { + const items = Array.from({ length: count }, (_, index) => ({ + key: `item-${index}`, + provider: 'github', + title: 'task', + subtitle: '', + status: 'open', + updatedAt: new Date(1700000000000 - index * 100000).toISOString(), + source: { repoId: `repo-${index % 25}`, repoName: `Repository ${index % 25}` } + })) + for (const sort of ['updated', 'repository']) { + const arms = { + before: () => + [...items].sort( + sort === 'repository' + ? (a, b) => before.compareTasksByRepository(a, b, repos) + : before.compareTasksByUpdated + ), + after: () => after.sortMobileTaskItems(items, sort, repos) + } + assert.deepEqual(arms.after(), arms.before()) + const iterations = count < 100 ? 100 : 10 + function run(arm) { + const start = performance.now() + for (let i = 0; i < iterations; i++) { + arms[arm]() + } + return (performance.now() - start) / iterations + } + const samples = { before: [], after: [] } + run('before') + run('after') + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + function median(values) { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + const dateParses = {} + const nativeParse = Date.parse + for (const arm of ['before', 'after']) { + let calls = 0 + Date.parse = (value) => { + calls++ + return nativeParse(value) + } + try { + arms[arm]() + } finally { + Date.parse = nativeParse + } + dateParses[arm] = calls + } + results.push({ + count, + sort, + dateParses, + beforeMs: median(samples.before), + afterMs: median(samples.after), + samples + }) + } +} +console.log( + JSON.stringify({ baseline, node: process.version, platform: process.platform, results }, null, 2) +) diff --git a/config/scripts/native-provider-line-gate-benchmark.mjs b/config/scripts/native-provider-line-gate-benchmark.mjs new file mode 100644 index 00000000000..75194f2c3ac --- /dev/null +++ b/config/scripts/native-provider-line-gate-benchmark.mjs @@ -0,0 +1,320 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import path from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' + +// Pipe the baseline client module on stdin. Native process/filesystem operations are forbidden here. +const entry = path.resolve('src/main/computer/macos-native-provider-client.ts') +const sources = [readFileSync(0, 'utf8'), readFileSync(entry, 'utf8')] +assert(sources.every((source) => source.includes('export class MacOSNativeProviderClient'))) +async function load(source) { + const result = await build({ + entryPoints: [entry], + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: [ + { + name: 'native-client-receive-fixture', + setup(builder) { + builder.onLoad({ filter: /macos-native-provider-client\.ts$/ }, () => ({ + contents: `${source}\nexport { NativeProviderLineBuffer, consumeNativeProviderLines } from './macos-native-provider-transport';`, + loader: 'ts', + resolveDir: path.dirname(entry) + })) + builder.onResolve({ filter: /^node:(fs|child_process)$/ }, (args) => ({ + path: args.path, + namespace: 'forbidden-native-operation' + })) + builder.onLoad({ filter: /.*/, namespace: 'forbidden-native-operation' }, (args) => ({ + contents: `function forbidden() { throw new Error('Native operations are forbidden in this benchmark'); } + export { forbidden as ${ + args.path === 'node:fs' + ? 'chmodSync, forbidden as mkdtempSync, forbidden as rmSync, forbidden as writeFileSync, forbidden as existsSync' + : 'spawn' + } };`, + loader: 'js' + })) + } + } + ] + }) + const bundled = `${result.outputFiles[0].text}\n//# sourceURL=native-provider-line-gate-benchmark-bundle.js` + return import(`data:text/javascript;base64,${Buffer.from(bundled).toString('base64')}`) +} +const modules = await Promise.all(sources.map(load)) + +class FixtureSocket { + destroyed = false + writes = [] + write(line) { + this.writes.push(line) + } + end() { + this.destroyed = true + } + destroy() { + this.destroyed = true + } +} + +function clientFixture(module) { + const client = new module.MacOSNativeProviderClient() + const socket = new FixtureSocket() + client.socket = socket + return { client, socket, stale: new FixtureSocket(), events: [] } +} + +function state(fixture) { + const { client, socket, events } = fixture + return { + buffered: + typeof client.socketBuffer === 'string' ? client.socketBuffer : client.socketBuffer.pending, + pending: [...client.pending.keys()], + active: client.socket === socket, + generation: client.socketStartGeneration, + destroyed: socket.destroyed, + writes: socket.writes, + events + } +} + +function register(fixture, id, throwCallback) { + fixture.client.pending.set(id, { + timer: undefined, + resolve(value) { + fixture.events.push(['resolve', id, value]) + if (throwCallback) { + throw new Error('fixture callback failure') + } + }, + reject(error) { + fixture.events.push(['reject', id, error.code, error.message]) + if (throwCallback) { + throw new Error('fixture callback failure') + } + } + }) +} + +let seed = 0x18c0ffee +function random(max) { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + return (seed >>> 8) % max +} +for (let trace = 0; trace < 2000; trace++) { + const fixtures = modules.map(clientFixture) + let remainder = '' + for (let step = 0; step < 40; step++) { + const op = random(20) + const id = random(8) + const throwCallback = random(12) === 0 + if (!remainder) { + remainder = [ + `${JSON.stringify({ id, ok: true, result: { text: 'Unicode 界😀', value: step } })}\n`, + `${JSON.stringify({ id, ok: false, error: { code: 'fixture', message: 'failed' } })}\r\n`, + `${JSON.stringify({ id, ok: false })}\n`, + ' \t\r\n', + 'invalid json\n', + 'null\n', + '\ud83d\udc00\n' + ][random(7)] + } + const length = random(remainder.length + 1) + const chunk = remainder.slice(0, length) + if (op > 5) { + remainder = remainder.slice(length) + } + for (const fixture of fixtures) { + const { client, socket } = fixture + try { + if (op === 0) { + client.shutdown() + } else if (op === 1) { + client.handleSocketClose(socket) + } else if (op === 2) { + client.handleTransportError(socket, new Error('fixture transport error')) + } else if (op === 3) { + client.invalidateActiveSocketAfterWriteFailure(socket, new Error('fixture write error')) + } else if (op === 4) { + fixture.stale = socket + fixture.socket = new FixtureSocket() + client.socket = fixture.socket + } else if (op === 5) { + register(fixture, id, throwCallback) + } else { + client.handleSocketData(op === 6 ? fixture.stale : socket, chunk) + } + } catch (error) { + fixture.events.push(['throw', error.name, error.message]) + } + } + assert.deepEqual(state(fixtures[1]), state(fixtures[0])) + } +} +console.log('2,000 actual client receive/lifecycle traces / 80,000 commands match') + +for (let trace = 0; trace < 1000; trace++) { + const fixtures = modules.map(clientFixture) + const failThird = random(2) === 0 + for (const fixture of fixtures) { + for (let id = 1; id <= 4; id++) { + register(fixture, id, id === 3 && failThird) + } + } + const input = [ + JSON.stringify({ id: 1, ok: true, result: { text: `界😀 ${trace}` } }), + JSON.stringify({ id: 2, ok: false, error: { code: 'fixture', message: 'failed' } }), + JSON.stringify({ id: 3, ok: true, result: trace }), + JSON.stringify({ id: 4, ok: true, result: 'final reply' }), + '' + ].join('\n') + let offset = 0 + while (offset < input.length) { + const length = 1 + random(80) + const chunk = input.slice(offset, offset + length) + offset += length + for (const fixture of fixtures) { + try { + fixture.client.handleSocketData(fixture.socket, chunk) + } catch (error) { + fixture.events.push(['throw', error.name, error.message]) + } + } + assert.deepEqual(state(fixtures[1]), state(fixtures[0])) + } + for (const fixture of fixtures) { + fixture.client.handleSocketData(fixture.socket, '') + assert.equal(fixture.client.pending.size, 0) + assert.deepEqual(fixture.events.at(-1), ['resolve', 4, 'final reply']) + } + assert.deepEqual(state(fixtures[1]), state(fixtures[0])) +} +console.log('1,000 fragmented multi-reply client journeys / 4,000 request settlements match') + +class BaselineBuffer { + pending = '' + push(chunk, onLine) { + this.pending += chunk + this.pending = modules[0].consumeNativeProviderLines(this.pending, onLine) + } + clear() { + this.pending = '' + } +} +for (let trace = 0; trace < 3000; trace++) { + const buffers = [new BaselineBuffer(), new modules[1].NativeProviderLineBuffer()] + const events = [[], []] + for (let step = 0; step < 30; step++) { + const clear = random(25) === 0 + const fail = random(10) === 0 + const chunk = ['abc', '\n', '\r\n', '\ud83d', '\udc00', '\n\n', '界', '', 'ok\nfault\npartial'][ + random(9) + ] + buffers.forEach((buffer, index) => { + if (clear) { + buffer.clear() + } + try { + buffer.push(chunk, (line) => { + events[index].push(line) + if (fail) { + throw new Error('fixture callback failure') + } + }) + } catch (error) { + events[index].push({ error: error.message }) + } + }) + assert.deepEqual(events[1], events[0]) + assert.equal(buffers[1].pending, buffers[0].pending) + } +} +console.log('3,000 actual line-buffer traces / 90,000 feeds match') + +function receiveArm(module) { + const fixture = clientFixture(module) + return (chunks) => { + let result + fixture.client.pending.set(1, { + timer: undefined, + resolve: (value) => { + result = value + }, + reject: (error) => { + throw error + } + }) + for (const chunk of chunks) { + fixture.client.handleSocketData(fixture.socket, chunk) + } + return result + } +} +function sample(arm, input, repeats) { + const start = performance.now() + let result + for (let i = 0; i < repeats; i++) { + result = arm(input) + } + return { elapsed: (performance.now() - start) / repeats, result } +} + +console.log( + JSON.stringify({ + node: process.version, + platform: process.platform, + arch: process.arch, + unit: 'ms', + pairs: 8 + }) +) +for (const [size, chunkBytes] of [ + [64, 65536], + [120000, 65536], + [1200000, 65536], + [1200000, 4096], + [4800000, 65536], + [1200000, Number.POSITIVE_INFINITY] +]) { + const expected = { screenshot: { data: 'A'.repeat(size) }, text: 'fixture' } + const input = `${JSON.stringify({ id: 1, ok: true, result: expected })}\n` + const chunks = [] + for (let offset = 0; offset < input.length; offset += chunkBytes) { + chunks.push(input.slice(offset, offset + chunkBytes)) + } + const arms = modules.map(receiveArm) + for (const arm of arms) { + assert.deepEqual(arm(chunks), expected) + const until = performance.now() + 150 + while (performance.now() < until) { + sample(arm, chunks, 1) + } + } + const repeats = Math.max(3, Math.min(100000, Math.ceil(50 / sample(arms[0], chunks, 1).elapsed))) + /** @type {number[][]} */ + const times = [[], []] + for (let pair = 0; pair < 8; pair++) { + for (const index of pair % 2 ? [1, 0] : [0, 1]) { + const result = sample(arms[index], chunks, repeats) + assert.deepEqual(result.result, expected) + times[index].push(result.elapsed) + } + } + const median = times.map((values) => { + values.sort((a, b) => a - b) + return (values[3] + values[4]) / 2 + }) + console.log( + JSON.stringify({ + size, + chunkBytes: Number.isFinite(chunkBytes) ? chunkBytes : 'whole frame', + chunks: chunks.length, + repeats, + median, + times + }) + ) +} diff --git a/config/scripts/package-electron-runtime-contract.test.mjs b/config/scripts/package-electron-runtime-contract.test.mjs index aa34e043268..ee05682993c 100644 --- a/config/scripts/package-electron-runtime-contract.test.mjs +++ b/config/scripts/package-electron-runtime-contract.test.mjs @@ -19,24 +19,18 @@ describe('Electron runtime package contract', () => { }) it('keeps the native Windows registry addon optional and platform-gated', () => { - const rebuildScript = readFileSync( - join(projectDir, 'config/scripts/rebuild-native-deps.mjs'), - 'utf8' - ) - const ensureScript = readFileSync( - join(projectDir, 'config/scripts/ensure-native-runtime.mjs'), - 'utf8' - ) - expect(packageJson.optionalDependencies['windows-native-registry']).toBe('3.2.2') + const rebuildScript = readProject('config/scripts/rebuild-native-deps.mjs') + const ensureScript = readProject('config/scripts/ensure-native-runtime.mjs') + expect(packageJson.optionalDependencies['@orca/windows-registry']).toBe('workspace:*') // Why: pnpm installs optional target architectures on every host; the root // Windows-only rebuild owns this addon so macOS/Linux never run node-gyp for it. - expect(pnpmWorkspace.allowBuilds['windows-native-registry']).toBe(false) + expect(pnpmWorkspace.allowBuilds['@orca/windows-registry']).toBe(false) // Why assert the guard and the member separately: the list now carries more // than one addon, so pinning the whole literal only tested its formatting. expect(rebuildScript).toContain("rebuildPlatform === 'win32'") - expect(rebuildScript).toContain("'windows-native-registry'") + expect(rebuildScript).toContain("'@orca/windows-registry'") expect(ensureScript).toContain("process.platform === 'win32'") - expect(ensureScript).toContain("'windows-native-registry'") + expect(ensureScript).toContain("'@orca/windows-registry'") const packageTargets = { win32: createPackagedRuntimeNodeModuleResources('win32'), darwin: createPackagedRuntimeNodeModuleResources('darwin'), @@ -44,14 +38,14 @@ describe('Electron runtime package contract', () => { } expect(packageTargets.win32).toEqual( expect.arrayContaining([ - expect.objectContaining({ to: join('node_modules', 'windows-native-registry') }), + expect.objectContaining({ to: join('node_modules', '@orca', 'windows-registry') }), expect.objectContaining({ to: join('node_modules', 'node-addon-api') }) ]) ) for (const platform of ['darwin', 'linux']) { expect(packageTargets[platform]).not.toEqual( expect.arrayContaining([ - expect.objectContaining({ to: join('node_modules', 'windows-native-registry') }) + expect.objectContaining({ to: join('node_modules', '@orca', 'windows-registry') }) ]) ) } diff --git a/config/scripts/plugin-command-bindings-benchmark.mjs b/config/scripts/plugin-command-bindings-benchmark.mjs new file mode 100644 index 00000000000..58b3dad8cab --- /dev/null +++ b/config/scripts/plugin-command-bindings-benchmark.mjs @@ -0,0 +1,101 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] +if (!baseline) { + throw new Error('Usage: node config/scripts/plugin-command-bindings-benchmark.mjs ') +} +async function load(file, contents, name) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + logLevel: 'silent', + tsconfigRaw: {}, + banner: { + js: "import { createRequire as benchmarkRequire } from 'node:module'; import { resolve as benchmarkPath } from 'node:path'; const require = benchmarkRequire(benchmarkPath('package.json'));" + } + }) + return ( + await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) + )[name] +} +const file = 'src/main/plugins/plugin-command-registry.ts' +const before = await load( + file, + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }), + 'PluginCommandRegistry' +) +const after = await load(file, readFileSync(file, 'utf8'), 'PluginCommandRegistry') +const results = [] +for (const count of [1, 16, 64, 256]) { + const plugins = [ + { + pluginKey: 'sample.commands', + manifest: { + contributes: { + commands: Array.from({ length: count }, (_, index) => ({ + id: `command-${index}`, + title: `Command ${index}`, + action: 'view.tasks' + })), + keybindings: Array.from({ length: Math.min(count, 104) }, (_, index) => ({ + command: `command-${index}`, + key: `Mod+${Math.floor(index / 26) & 1 ? 'Alt+' : ''}${Math.floor(index / 26) & 2 ? 'Shift+' : ''}${String.fromCharCode(65 + (index % 26))}` + })) + } + } + } + ] + const arms = { before: new before(), after: new after() } + for (const platform of ['darwin', 'linux', 'win32']) { + for (const arm of Object.values(arms)) { + arm.reconcile(plugins, () => true, {}, platform) + } + const snapshot = (registry) => ({ + active: registry.list(), + previews: plugins.map((plugin) => registry.preview(plugin.pluginKey)), + errors: plugins.map((plugin) => registry.error(plugin.pluginKey)) + }) + assert.deepEqual(snapshot(arms.after), snapshot(arms.before)) + } + const iterations = 100 + function run(arm) { + const start = performance.now() + for (let i = 0; i < iterations; i++) { + arms[arm].reconcile(plugins, () => true, {}, 'linux') + } + return (performance.now() - start) / iterations + } + const samples = { before: [], after: [] } + run('before') + run('after') + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + function median(values) { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + results.push({ + commands: count, + bindings: Math.min(count, 104), + beforeMs: median(samples.before), + afterMs: median(samples.after), + samples + }) +} +console.log( + JSON.stringify({ baseline, node: process.version, platform: process.platform, results }, null, 2) +) diff --git a/config/scripts/plugin-message-budget-benchmark.mjs b/config/scripts/plugin-message-budget-benchmark.mjs new file mode 100644 index 00000000000..605eee85ea6 --- /dev/null +++ b/config/scripts/plugin-message-budget-benchmark.mjs @@ -0,0 +1,95 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] ?? '20ab9950654' +const file = 'src/shared/plugins/plugin-panel-message-budget.ts' +async function load(contents) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false + }) + return import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) +} +const before = await load( + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }) +) +const after = await load(readFileSync(file, 'utf8')) +const results = [] +for (const count of [5, 1000, 100000]) { + const entries = Array.from({ length: count }, (_, i) => [`key-${i}`, `value-${i}`]) + for (const [kind, value] of [ + ['array', entries.map(([, value]) => value)], + ['map', new Map(entries)], + ['set', new Set(entries.map(([, value]) => value))], + ['object', Object.fromEntries(entries)] + ]) { + const input = structuredClone(value) + for (const cap of [0, 1, 64, 1024, 65536, Infinity]) { + assert.equal( + after.structuredCloneMessageBytes(input, cap), + before.structuredCloneMessageBytes(input, cap) + ) + } + const arms = { before, after } + const iterations = count < 100 ? 1000 : 10 + const run = (arm) => { + global.gc?.() + const start = performance.now() + const cpuStart = process.cpuUsage() + for (let i = 0; i < iterations; i++) { + arms[arm].structuredCloneMessageBytes(input) + } + const cpu = process.cpuUsage(cpuStart) + return { + ms: (performance.now() - start) / iterations, + cpuMs: (cpu.user + cpu.system) / 1000 / iterations + } + } + for (let i = 0; i < 3; i++) { + run('before') + run('after') + } + const samples = { before: [], after: [] } + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + const median = (values) => { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + results.push({ + count, + kind, + beforeMs: median(samples.before.map((sample) => sample.ms)), + beforeCpuMs: median(samples.before.map((sample) => sample.cpuMs)), + afterMs: median(samples.after.map((sample) => sample.ms)), + afterCpuMs: median(samples.after.map((sample) => sample.cpuMs)), + samples + }) + } +} +console.log( + JSON.stringify( + { + baseline, + node: process.version, + platform: process.platform, + forcedGc: Boolean(global.gc), + results + }, + null, + 2 + ) +) diff --git a/config/scripts/plugin-shortcut-conflicts-benchmark.mjs b/config/scripts/plugin-shortcut-conflicts-benchmark.mjs new file mode 100644 index 00000000000..c7dd5d3c60e --- /dev/null +++ b/config/scripts/plugin-shortcut-conflicts-benchmark.mjs @@ -0,0 +1,95 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] +if (!baseline) { + throw new Error( + 'Usage: node config/scripts/plugin-shortcut-conflicts-benchmark.mjs ' + ) +} +async function load(file, contents, name) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + logLevel: 'silent', + tsconfigRaw: {}, + banner: { + js: "import { createRequire as benchmarkRequire } from 'node:module'; import { resolve as benchmarkPath } from 'node:path'; const require = benchmarkRequire(benchmarkPath('package.json'));" + } + }) + return ( + await import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) + )[name] +} +const file = 'src/main/plugins/plugin-command-registry.ts' +const before = await load( + file, + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' }), + 'PluginCommandRegistry' +) +const after = await load(file, readFileSync(file, 'utf8'), 'PluginCommandRegistry') +const results = [] +for (const count of [1, 8, 32, 128]) { + const plugins = Array.from({ length: count }, (_, index) => ({ + pluginKey: `sample.plugin-${index}`, + manifest: { + contributes: { + commands: [ + { + id: 'open', + title: 'Open', + action: 'view.tasks', + context: index % 2 ? 'worktree' : 'global' + } + ], + keybindings: [{ command: 'open', key: 'Mod+Alt+T' }] + } + } + })) + const arms = { before: new before(), after: new after() } + for (const platform of ['darwin', 'linux', 'win32']) { + for (const arm of Object.values(arms)) { + arm.reconcile(plugins, () => true, {}, platform) + } + const snapshot = (registry) => ({ + active: registry.list(), + previews: plugins.map((plugin) => registry.preview(plugin.pluginKey)), + errors: plugins.map((plugin) => registry.error(plugin.pluginKey)) + }) + assert.deepEqual(snapshot(arms.after), snapshot(arms.before)) + } + const iterations = 100 + function run(arm) { + const start = performance.now() + for (let i = 0; i < iterations; i++) { + arms[arm].reconcile(plugins, () => true, {}, 'linux') + } + return (performance.now() - start) / iterations + } + const samples = { before: [], after: [] } + run('before') + run('after') + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + function median(values) { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + results.push({ count, beforeMs: median(samples.before), afterMs: median(samples.after), samples }) +} +console.log( + JSON.stringify({ baseline, node: process.version, platform: process.platform, results }, null, 2) +) diff --git a/config/scripts/pr-code-change-scope.mjs b/config/scripts/pr-code-change-scope.mjs index 8e88aa16df5..3b39bb34275 100644 --- a/config/scripts/pr-code-change-scope.mjs +++ b/config/scripts/pr-code-change-scope.mjs @@ -215,6 +215,7 @@ const WINDOWS_PACKAGE_TESTS = [ ...LINUX_PACKAGE_TESTS, 'config/scripts/rebuild-native-deps.test.mjs', 'config/scripts/rebuild-native-deps-windows-process-tree.test.mjs', + 'src/main/windows-registry-addon.test.ts', 'src/main/providers/windows-conpty-wide-char-duplication.node-pty.test.ts', 'src/main/providers/pty-repaint-wide-char-buffer.node-pty.test.ts', 'src/shared/child-process/windows-command-line.win32.test.ts', diff --git a/config/scripts/pr-workflow-parallelism.test.mjs b/config/scripts/pr-workflow-parallelism.test.mjs index 92d4fe4c26b..2074d8c631d 100644 --- a/config/scripts/pr-workflow-parallelism.test.mjs +++ b/config/scripts/pr-workflow-parallelism.test.mjs @@ -389,7 +389,7 @@ describe('PR workflow parallelism', () => { expect(cacheStep.with.key).toContain('config/scripts/ensure-native-runtime.mjs') expect(cacheStep.with.key).toContain('config/scripts/rebuild-native-deps.mjs') expect(cacheStep.with.path).toContain('node-pty@*/node_modules/node-pty/build') - expect(cacheStep.with.path).toContain('windows-native-registry@') + expect(cacheStep.with.path).toContain('@orca+windows-registry@') expect(cacheStep.with.path).toContain('@vscode+windows-process-tree@') expect(cacheStep.with['restore-keys']).toBeUndefined() } diff --git a/config/scripts/raw-markdown-comment-scan-benchmark.mjs b/config/scripts/raw-markdown-comment-scan-benchmark.mjs new file mode 100644 index 00000000000..cc6b5964e61 --- /dev/null +++ b/config/scripts/raw-markdown-comment-scan-benchmark.mjs @@ -0,0 +1,94 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] ?? '20ab9950654' +const file = 'src/renderer/src/components/editor/raw-markdown-html.ts' +async function load(contents) { + const result = await build({ + stdin: { + contents: `${contents}\nexport { createRichMarkdownEditorCodec } from './rich-markdown-source-transport'`, + loader: 'ts', + resolveDir: dirname(resolve(file)) + }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + banner: { + js: `import { createRequire } from 'node:module'; const require = createRequire(${JSON.stringify(resolve('package.json'))});` + } + }) + return import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) +} +const arms = { + before: await load( + execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8', windowsHide: true }) + ), + after: await load(readFileSync(file, 'utf8')) +} +const key = '0123456789abcdef0123456789abcdef' +const codecs = Object.fromEntries( + Object.entries(arms).map(([arm, module]) => [arm, module.createRichMarkdownEditorCodec(key)]) +) +const results = [] +for (const [name, input] of [ + ['plain', '# Heading\nOrdinary prose.'], + ['complete', 'before after text\n'.repeat(100)], + ['unclosed-1000', `prefix ${'${'\n## Review findings\n${'Code sample with details\n'.repeat(100)}` + ], + [ + '10000-lines', + `\n## Review findings\n${'Code sample with details\n'.repeat(10000)}` + ], + ['blank-10000-lines', '# > * - _ `\n'.repeat(10000)] +]) { + const comments = Array.from({ length: 10 }, (_, id) => ({ + id, + author: 'reviewer', + authorAvatarUrl: '', + createdAt: '', + url: '', + body + })) + const arms = { before, after } + assert.equal( + after.buildPRCommentBatchConversationReplyBody(comments), + before.buildPRCommentBatchConversationReplyBody(comments) + ) + const run = (arm) => { + global.gc?.() + const start = performance.now() + const cpuStart = process.cpuUsage() + for (let i = 0; i < 10; i++) { + arms[arm].buildPRCommentBatchConversationReplyBody(comments) + } + const cpu = process.cpuUsage(cpuStart) + return { ms: (performance.now() - start) / 10, cpuMs: (cpu.user + cpu.system) / 10000 } + } + run('before') + run('after') + const samples = { before: [], after: [] } + for (const pair of buildCounterbalancedSchedule(10, 'before', 'after')) { + for (const arm of pair) { + samples[arm].push(run(arm)) + } + } + const median = (values) => { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[4] + sorted[5]) / 2 + } + results.push({ + name, + beforeCpuMs: median(samples.before.map((s) => s.cpuMs)), + afterCpuMs: median(samples.after.map((s) => s.cpuMs)), + beforeMs: median(samples.before.map((s) => s.ms)), + afterMs: median(samples.after.map((s) => s.ms)), + samples + }) +} +console.log( + JSON.stringify({ baseline, node: process.version, platform: process.platform, results }, null, 2) +) diff --git a/config/scripts/rich-markdown-blank-run-benchmark.mjs b/config/scripts/rich-markdown-blank-run-benchmark.mjs new file mode 100644 index 00000000000..868a4c2ef6d --- /dev/null +++ b/config/scripts/rich-markdown-blank-run-benchmark.mjs @@ -0,0 +1,101 @@ +#!/usr/bin/env node +import assert from 'node:assert/strict' +import { readFile, mkdtemp, rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' +import { summarizeBenchmarkSamples } from './benchmark-sample-summary.mjs' + +const root = fileURLToPath(new URL('../..', import.meta.url)) +const entry = join(root, 'src/renderer/src/components/editor/raw-markdown-html.ts') +const source = await readFile(entry, 'utf8') +const cachedProbe = + /if \(index > fenceProbe\) \{[\s\S]*?fenceMatch = fencePrefix.exec\(normalizedContent\)\n \}/ +assert.match(source, cachedProbe) +const oldProbe = String.raw`fenceMatch = normalizedContent.slice(index).match(/^\s*(\x60{3,}|~{3,})/)` +const temp = await mkdtemp(join(tmpdir(), 'orca-rich-blank-bench-')) +try { + const scanners = {} + for (const arm of ['baseline', 'current']) { + const outfile = join(temp, `${arm}.cjs`) + await build({ + stdin: { + contents: `export { encodeRawMarkdownHtmlForRichEditor as encode } from './src/renderer/src/components/editor/raw-markdown-html'; export { createRichMarkdownEditorCodec as codec } from './src/renderer/src/components/editor/rich-markdown-source-transport';`, + resolveDir: root + }, + bundle: true, + platform: 'node', + format: 'cjs', + outfile, + plugins: + arm === 'baseline' + ? [ + { + name: 'old-probe', + setup(plugin) { + plugin.onLoad({ filter: /raw-markdown-html\.ts$/ }, () => ({ + contents: source.replace(cachedProbe, oldProbe), + loader: 'ts', + resolveDir: join(root, 'src/renderer/src/components/editor') + })) + } + } + ] + : [] + }) + const { encode, codec } = createRequire(import.meta.url)(outfile) + scanners[arm] = (content) => encode(content, codec('0'.repeat(32))) + } + const fragments = [ + '\n', + ' \r\n', + '\u00a0\u2028', + '```\n', + '~~~~\n', + '
\n', + '
\n', + '[[doc.md]]\n', + '`inline`\n', + 'prose\n' + ] + let seed = 42 + for (let sample = 0; sample < 256; sample++) { + let content = '' + for (let i = 0; i < 20; i++) { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0 + content += fragments[seed % fragments.length] + } + assert.equal(scanners.current(content), scanners.baseline(content)) + } + for (const [name, content] of [ + ['ordinary', 'ordinary prose\n'.repeat(10000)], + ['blank-100k', '\n'.repeat(100000)], + ['blank-before-fence', `${'\n'.repeat(30000)}\x60\x60\x60\n
\n\x60\x60\x60\n[[doc.md]]`] + ]) { + const samples = { baseline: [], current: [] } + let expected + for (const arms of buildCounterbalancedSchedule(2, 'baseline', 'current')) { + for (const arm of arms) { + const start = performance.now() + const result = scanners[arm](content) + samples[arm].push(performance.now() - start) + expected ??= result + assert.equal(result, expected) + } + } + console.log( + JSON.stringify({ + name, + bytes: Buffer.byteLength(content), + samples, + baseline: summarizeBenchmarkSamples(samples.baseline), + current: summarizeBenchmarkSamples(samples.current) + }) + ) + } +} finally { + await rm(temp, { recursive: true, force: true }) +} diff --git a/config/scripts/rich-markdown-comment-scan-benchmark.mjs b/config/scripts/rich-markdown-comment-scan-benchmark.mjs new file mode 100644 index 00000000000..29c52e4b761 --- /dev/null +++ b/config/scripts/rich-markdown-comment-scan-benchmark.mjs @@ -0,0 +1,104 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { performance } from 'node:perf_hooks' +import { build } from 'esbuild' +import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs' + +const baseline = process.argv[2] ?? '20ab9950654' +const file = 'src/renderer/src/components/editor/markdown-rich-mode.ts' +async function load(contents) { + const result = await build({ + stdin: { contents, loader: 'ts', resolveDir: dirname(resolve(file)) }, + bundle: true, + platform: 'node', + format: 'esm', + write: false, + plugins: [ + { + name: 'cached-round-trip', + setup(bundler) { + bundler.onResolve({ filter: /markdown-round-trip$|^@\/i18n\/i18n$/ }, (args) => ({ + path: args.path, + namespace: 'bench' + })) + bundler.onLoad({ filter: /.*/, namespace: 'bench' }, (args) => ({ + contents: args.path.endsWith('markdown-round-trip') + ? 'export const getRichMarkdownRoundTripOutput = (content) => content' + : 'export const translate = (_key, fallback) => fallback', + loader: 'js' + })) + } + } + ] + }) + return import( + `data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}` + ) +} +const arms = { + before: await load(execFileSync('git', ['show', `${baseline}:${file}`], { encoding: 'utf8' })), + after: await load(readFileSync(file, 'utf8')) +} +const results = [] +for (const [name, content] of [ + ['plain', '# Heading\nOrdinary prose with .'], + ['complete', 'text'], + ['unclosed-1000', '', null], + ['', 'html-or-jsx'], + ['tail', 'html-or-jsx'], + ['">', 'html-or-jsx'], + ['``', null], + ['```html\n\n```', null], + ['\n[a]: https://example.com', 'reference-links'], + ['\n[^a]: footnote', 'reference-links'] + ] as const)('preserves the decision for %j', (content, expected) => { + vi.mocked(getRichMarkdownRoundTripOutput).mockReturnValue(null) + expect(getMarkdownRichModeUnsupportedReason(content)).toBe(expected) + }) + + it('does not pass unclosed comment openers through a comment regex', () => { + const input = '' + ).length + expect(result).toBeNull() + expect(closerSearches).toBeLessThanOrEqual(1) + vi.mocked(getRichMarkdownRoundTripOutput).mockReturnValue( + input.replace('after', 'after') + ) + expect(getMarkdownRichModeUnsupportedReason(input)).toBe('html-or-jsx') + }) +}) diff --git a/src/renderer/src/components/editor/markdown-rich-line-scan.test.ts b/src/renderer/src/components/editor/markdown-rich-line-scan.test.ts new file mode 100644 index 00000000000..2cadf352b82 --- /dev/null +++ b/src/renderer/src/components/editor/markdown-rich-line-scan.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it, vi } from 'vitest' +import { getMarkdownRichModeEligibilityDecision } from './markdown-rich-mode' + +const decide = (content: string) => + getMarkdownRichModeEligibilityDecision({ content, sizeOverridden: false }) + +describe('rich Markdown line scanning', () => { + it('does not inspect every prose character to find newline boundaries', () => { + const content = 'Ordinary prose '.repeat(10_000) + const spy = vi.spyOn(String.prototype, 'charCodeAt') + let decision: ReturnType + let calls: number + try { + decision = decide(content) + calls = spy.mock.calls.length + } finally { + spy.mockRestore() + } + expect(decision).toEqual({ exceedsSizeLimit: false, unsupportedReason: null }) + expect(calls).toBeLessThan(10) + }) + + it.each(['\n', '\r\n'])( + 'preserves fences and reference visibility with %j endings', + (newline) => { + for (const fence of ['```', '~~~']) { + const protectedSource = [`${fence}md`, '[ref]: /hidden', fence].join(newline) + expect(decide(protectedSource).unsupportedReason).toBeNull() + expect(decide(protectedSource + newline).unsupportedReason).toBeNull() + expect(decide(`${protectedSource}${newline}[ref]: /visible`).unsupportedReason).toBe( + 'reference-links' + ) + } + } + ) + + it.each(['', '\n', '\r', '\r\n', '`[ref]: /hidden`', '```\n[ref]: /hidden\r'])( + 'preserves empty, final-CR, inline-code and unclosed-fence input %j', + (content) => expect(decide(content).unsupportedReason).toBeNull() + ) +}) diff --git a/src/renderer/src/components/editor/markdown-rich-mode.ts b/src/renderer/src/components/editor/markdown-rich-mode.ts index 142e0cea560..4849380f8fe 100644 --- a/src/renderer/src/components/editor/markdown-rich-mode.ts +++ b/src/renderer/src/components/editor/markdown-rich-mode.ts @@ -48,7 +48,7 @@ const UNSUPPORTED_PATTERNS: UnsupportedMatch[] = [ // Why: the rich editor preserves common embedded markup via placeholder // tokens before parsing, but any HTML shape that still fails round-trip // must fall back instead of risking silent source corruption. - pattern: /<\/?[A-Za-z][\w.:-]*(?:\s[^<>]*)?\/?>|/ + pattern: /<\/?[A-Za-z][\w.:-]*(?:\s[^<>]*)?\/?>/ }, { reason: 'reference-links', @@ -157,6 +157,11 @@ export function getMarkdownRichModeEligibility(params: { } function hasHtmlOrJsx(content: string, pattern: RegExp): boolean { + // A missing closer after the first opener rules out every later opener. + const commentStart = content.indexOf('', commentStart + 4)) { + return true + } for (const match of content.matchAll(new RegExp(pattern, 'g'))) { if (isHtmlOrJsxFragment(match[0])) { return true @@ -166,7 +171,7 @@ function hasHtmlOrJsx(content: string, pattern: RegExp): boolean { } function isHtmlOrJsxFragment(fragment: string): boolean { - if (fragment.startsWith('') for (let index = 0; index < content.length; index++) { if (content.charCodeAt(index) !== 60) { continue @@ -231,7 +236,7 @@ function forEachEmbeddedHtmlFragment( let fragmentEnd: number | null = null if (content.startsWith('', index + 4) + const commentEnd = index + 4 <= lastCommentClose ? content.indexOf('-->', index + 4) : -1 fragmentEnd = commentEnd === -1 ? null : commentEnd + 3 } else { fragmentEnd = getHtmlTagEnd(content, index) diff --git a/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.offset-scan.test.ts b/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.offset-scan.test.ts index e0e703a23e7..e333f7801a9 100644 --- a/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.offset-scan.test.ts +++ b/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.offset-scan.test.ts @@ -76,6 +76,14 @@ function referenceDecorationRanges(content: string): IRange[] { const CORPUS: { name: string; content: string }[] = [ { name: 'empty', content: '' }, + { + name: 'Unicode whitespace before fences', + content: '\u00a0\u2028```\n[[hidden]]\n```\n[[shown]]' + }, + { + name: 'indented blank run before fences', + content: `${`${' '.repeat(80)}\r\n`.repeat(1000)}\`\`\`\n[[hidden]]\n\`\`\`\n[[shown]]` + }, { name: 'no links', content: '# Title\n\nJust prose.\n' }, { name: 'single link', content: '# Title\n\nSee [[notes.md]] for details.\n' }, { name: 'two links on one line', content: 'See [[a.md]] and [[b.md]].\n' }, diff --git a/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.ts b/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.ts index e81ba97ce1b..457213e0c12 100644 --- a/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.ts +++ b/src/renderer/src/components/editor/monaco-markdown-doc-link-decorations.ts @@ -41,12 +41,11 @@ function isInsideSpan(index: number, spans: number[]): boolean { return false } -const FENCE_PREFIX_RE = /\s*(?:```|~~~)/y +const FENCE_PREFIX_RE = /[^\S\n]*(?:```|~~~)/y function startsCodeFence(content: string, lineStart: number, lineEnd: number): boolean { FENCE_PREFIX_RE.lastIndex = lineStart - // Why: a sticky `\s*` run can cross the newline into the next line, so an - // out-of-line match is rejected to stay identical to the old per-line regex. + // Bound whitespace to this line so blank runs cannot trigger repeated suffix scans. return FENCE_PREFIX_RE.test(content) && FENCE_PREFIX_RE.lastIndex <= lineEnd } diff --git a/src/renderer/src/components/editor/raw-markdown-comment-scan.test.ts b/src/renderer/src/components/editor/raw-markdown-comment-scan.test.ts new file mode 100644 index 00000000000..81e6990f058 --- /dev/null +++ b/src/renderer/src/components/editor/raw-markdown-comment-scan.test.ts @@ -0,0 +1,45 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { encodeRawMarkdownHtmlForRichEditor } from './raw-markdown-html' +import { createRichMarkdownEditorCodec } from './rich-markdown-source-transport' + +const key = '0123456789abcdef0123456789abcdef' +afterEach(() => vi.restoreAllMocks()) + +describe('raw Markdown HTML comment scanning', () => { + it('does not repeatedly match a suffix with no comment closer', () => { + const codec = createRichMarkdownEditorCodec(key) + const input = `prefix ${' after ')} after '])( + 'keeps protected or incomplete text literal: %j', + (input) => { + expect(encodeRawMarkdownHtmlForRichEditor(input, createRichMarkdownEditorCodec(key))).toBe( + input + ) + } + ) + it('retains the existing block-only handling of an overlapping marker', () => { + const codec = createRichMarkdownEditorCodec(key) + expect(encodeRawMarkdownHtmlForRichEditor('', codec)).toBe( + codec.transport.create('block-html', '') + ) + }) +}) diff --git a/src/renderer/src/components/editor/raw-markdown-html.blank-run.test.ts b/src/renderer/src/components/editor/raw-markdown-html.blank-run.test.ts new file mode 100644 index 00000000000..1ee24f73003 --- /dev/null +++ b/src/renderer/src/components/editor/raw-markdown-html.blank-run.test.ts @@ -0,0 +1,40 @@ +import { runInNewContext } from 'node:vm' +import { describe, expect, it } from 'vitest' +import { encodeRawMarkdownHtmlForRichEditor } from './raw-markdown-html' +import { createRichMarkdownEditorCodec } from './rich-markdown-source-transport' + +function encodeWithDeadline(content: string): string { + // Interrupt a quadratic regression without hanging the synchronous test worker. + return runInNewContext( + 'encode(content, codec)', + { + encode: encodeRawMarkdownHtmlForRichEditor, + content, + codec: createRichMarkdownEditorCodec('0'.repeat(32)) + }, + { timeout: 1500 } + ) +} + +describe('rich Markdown blank-run encoding', () => { + it('preserves a long blank document without repeatedly searching its suffix', () => { + const content = '\n'.repeat(100_000) + expect(encodeWithDeadline(content)).toBe(content) + }) + + it('inlines a reference definition after a long blank prefix without rescanning it', () => { + const blankPrefix = '\n'.repeat(100_000) + const content = `${blankPrefix}[Docs]\n\n[docs]: https://example.com/docs\n` + expect(encodeWithDeadline(content)).toBe(`${blankPrefix}[Docs](https://example.com/docs)\n\n`) + }) + + it('preserves code and surrounding HTML after a long blank prefix', () => { + const blankPrefix = '\n'.repeat(100_000) + const content = '```\n
inside
\n```\nafter' + const codec = createRichMarkdownEditorCodec('0'.repeat(32)) + const expected = encodeRawMarkdownHtmlForRichEditor(content, codec) + expect(expected).toContain('
inside
') + expect(expected).not.toContain('after') + expect(encodeWithDeadline(blankPrefix + content)).toBe(blankPrefix + expected) + }) +}) diff --git a/src/renderer/src/components/editor/raw-markdown-html.ts b/src/renderer/src/components/editor/raw-markdown-html.ts index cde5311e80b..1262d5254ab 100644 --- a/src/renderer/src/components/editor/raw-markdown-html.ts +++ b/src/renderer/src/components/editor/raw-markdown-html.ts @@ -59,20 +59,27 @@ export function encodeRawMarkdownHtmlForRichEditor( { htmlSuperscriptLinks = false }: { htmlSuperscriptLinks?: boolean } = {} ): string { const normalizedContent = normalizeMarkdownReferenceLinks(content) + const lastCommentClose = normalizedContent.lastIndexOf('-->') const { transport } = codec let index = 0 let isLineStart = true let activeFence: '`' | '~' | null = null let activeFenceLength = 0 let result = '' + const nonWhitespace = /\S/g + const fencePrefix = /(`{3,}|~{3,})/y + let fenceProbe = -1 + let fenceMatch: RegExpExecArray | null = null while (index < normalizedContent.length) { if (isLineStart) { - // Why: only line starts inspect the rest of the line, so slicing the suffix on every - // character (one throwaway string per char) is pure waste — compute it here. On a large - // doc this drops O(n) suffix allocations from the rich-editor open path (#7056). - const lineRest = normalizedContent.slice(index) - const fenceMatch = lineRest.match(/^\s*(`{3,}|~{3,})/) + // Reuse the lookahead across blank lines, preserving cross-line fence semantics. + if (index > fenceProbe) { + nonWhitespace.lastIndex = index + fenceProbe = nonWhitespace.exec(normalizedContent)?.index ?? normalizedContent.length + fencePrefix.lastIndex = fenceProbe + fenceMatch = fencePrefix.exec(normalizedContent) + } if (fenceMatch) { const fenceChar = fenceMatch[1][0] as '`' | '~' const fenceLength = fenceMatch[1].length @@ -175,7 +182,11 @@ export function encodeRawMarkdownHtmlForRichEditor( continue } } - const inlineHtml = matchInlineHtml(normalizedContent.slice(index)) + // An unterminated comment cannot match; later tags must still be encoded. + const inlineHtml = + normalizedContent.startsWith('/g, ' ') + .split('\n') + .map((line) => + line + .replace(/^[\s>#*\-_`]+/, '') + .replace(/\s+/g, ' ') + .trim() + ) + .find((line) => line.length > 0) + if (!line) { + return 'comment' + } + return `comment — ${line.length > 72 ? `${line.slice(0, 71).trimEnd()}…` : line}` +} +afterEach(() => vi.restoreAllMocks()) + +describe('review acknowledgement summary', () => { + it.each([ + '', + '\n\r\n\t', + '# > ** _ - `\n\nReadable', + '\n## Hello\r\nignored', + 'onetwo\nignored', + 'visible -->', + 'a\rb\nc', + '\u00a0##\u2028Hello\u2029world', + 'a'.repeat(71), + 'a'.repeat(72), + 'a'.repeat(73), + `${'a'.repeat(70)} b`, + `${'a'.repeat(70)}😀tail`, + '\n\n', + 'first\n\nlast' + ])('preserves the existing label for %j', (body) => { + expect(describePRCommentAckTarget(comment(body))).toBe(originalSummary(body)) + }) + + it('skips tail normalization and full-document line splitting', () => { + const input = comment(`## Heading\n${'tail with whitespace\n'.repeat(10_000)}`) + const replace = vi.spyOn(String.prototype, 'replace') + const split = vi.spyOn(String.prototype, 'split') + const actual = describePRCommentAckTarget(input) + const replaces = replace.mock.calls.length + const splits = split.mock.calls.length + expect(actual).toBe('comment — Heading') + expect(replaces).toBe(3) + expect(splits).toBe(0) + }) +}) diff --git a/src/renderer/src/components/right-sidebar/pr-comment-fixing-reply-body.ts b/src/renderer/src/components/right-sidebar/pr-comment-fixing-reply-body.ts index 5122a653ef9..5a12b6bc17a 100644 --- a/src/renderer/src/components/right-sidebar/pr-comment-fixing-reply-body.ts +++ b/src/renderer/src/components/right-sidebar/pr-comment-fixing-reply-body.ts @@ -24,22 +24,26 @@ const ACK_SNIPPET_MAX_LENGTH = 72 /** First readable line of a comment body, minus HTML comments and markdown markers. */ function summarizePRCommentBody(body: string): string { - const line = body - .replace(//g, ' ') - .split('\n') - .map((candidate) => - candidate - .replace(/^[\s>#*\-_`]+/, '') - .replace(/\s+/g, ' ') - .trim() - ) - .find((candidate) => candidate.length > 0) - if (!line) { - return '' + const cleaned = body.replace(//g, ' ') + let start = 0 + while (start <= cleaned.length) { + const newline = cleaned.indexOf('\n', start) + const line = cleaned + .slice(start, newline === -1 ? cleaned.length : newline) + .replace(/^[\s>#*\-_`]+/, '') + .replace(/\s+/g, ' ') + .trim() + if (line) { + return line.length > ACK_SNIPPET_MAX_LENGTH + ? `${line.slice(0, ACK_SNIPPET_MAX_LENGTH - 1).trimEnd()}…` + : line + } + if (newline === -1) { + break + } + start = newline + 1 } - return line.length > ACK_SNIPPET_MAX_LENGTH - ? `${line.slice(0, ACK_SNIPPET_MAX_LENGTH - 1).trimEnd()}…` - : line + return '' } /** Short "what this was" label so the batched reply names each item without quoting it whole. */ diff --git a/src/renderer/src/components/sidebar/smart-sort.ts b/src/renderer/src/components/sidebar/smart-sort.ts index ffebad8cf75..012012eb53b 100644 --- a/src/renderer/src/components/sidebar/smart-sort.ts +++ b/src/renderer/src/components/sidebar/smart-sort.ts @@ -188,9 +188,9 @@ export function sortWorktreesSmart( // Why: `tabHasLivePty` (over `ptyIdsByTabId`) is the source of truth for // liveness — slept terminals retain `tab.ptyId` as a wake hint, so reading // it directly would falsely keep cold-start ordering off after restart. - const hasAnyLivePty = Object.values(tabsByWorktree) - .flat() - .some((tab) => tabHasLivePty(ptyIdsByTabId, tab.id)) + const hasAnyLivePty = Object.values(tabsByWorktree).some((tabs) => + tabs.some((tab) => tabHasLivePty(ptyIdsByTabId, tab.id)) + ) const now = Date.now() const labels = buildWorktreeSortLabels(worktrees) diff --git a/src/renderer/src/components/sidebar/worktree-card-surface.tsx b/src/renderer/src/components/sidebar/worktree-card-surface.tsx index a045a7b2d32..baf68056993 100644 --- a/src/renderer/src/components/sidebar/worktree-card-surface.tsx +++ b/src/renderer/src/components/sidebar/worktree-card-surface.tsx @@ -53,7 +53,7 @@ export function WorktreeCardSurface({ card }: { card: WorktreeCardController }): // Why: the live data attribute updates before React state during navigation, // so it must own the complete active style without stale utility classes. isLineageDropTarget - ? 'border border-accent-foreground/20 bg-accent/80' + ? 'border border-worktree-sidebar-foreground/40 bg-worktree-sidebar-accent text-worktree-sidebar-accent-foreground ring-1 ring-inset ring-worktree-sidebar-ring/60' : isActiveSurface ? 'border border-transparent' : isMultiSelected @@ -72,7 +72,10 @@ export function WorktreeCardSurface({ card }: { card: WorktreeCardController }): isRuntimeDisconnected && !isDeleting && 'opacity-60' )} data-worktree-card-surface="true" - data-worktree-card-active={isActiveSurface ? activeSurfaceVariant : undefined} + data-worktree-card-active={ + isActiveSurface && !isLineageDropTarget ? activeSurfaceVariant : undefined + } + data-worktree-lineage-drop-target={isLineageDropTarget || undefined} onClick={handleClick} onDoubleClick={affiliateListMode ? undefined : handleDoubleClick} draggable={!affiliateListMode && nativeDragEnabled && !isDeleting && !titleRenaming} diff --git a/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.test.ts b/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.test.ts index dc8785f7b84..ba74bec19e1 100644 --- a/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.test.ts +++ b/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.test.ts @@ -14,25 +14,41 @@ describe('isWorktreeLineageDropZoneHit', () => { it('keeps the top and bottom of a card available for reorder drops', () => { const rect = { top: 100, bottom: 200 } as DOMRect - expect(isWorktreeLineageDropZoneHit({ pointerY: 120, rect })).toBe(false) + expect(isWorktreeLineageDropZoneHit({ pointerY: 107, rect })).toBe(false) expect(isWorktreeLineageDropZoneHit({ pointerY: 150, rect })).toBe(true) - expect(isWorktreeLineageDropZoneHit({ pointerY: 180, rect })).toBe(false) + expect(isWorktreeLineageDropZoneHit({ pointerY: 193, rect })).toBe(false) }) - it('caps the parent-drop band on tall cards', () => { + it('makes most of tall cards available for nesting', () => { const rect = { top: 0, bottom: 180 } as DOMRect - expect(isWorktreeLineageDropZoneHit({ pointerY: 67, rect })).toBe(false) + expect(isWorktreeLineageDropZoneHit({ pointerY: 8, rect })).toBe(true) expect(isWorktreeLineageDropZoneHit({ pointerY: 90, rect })).toBe(true) - expect(isWorktreeLineageDropZoneHit({ pointerY: 113, rect })).toBe(false) + expect(isWorktreeLineageDropZoneHit({ pointerY: 172, rect })).toBe(true) + }) + + it('scales down reorder gutters for compact cards', () => { + const rect = { top: 100, bottom: 120 } + + expect(isWorktreeLineageDropZoneHit({ pointerY: 103, rect })).toBe(false) + expect(isWorktreeLineageDropZoneHit({ pointerY: 104, rect })).toBe(true) + expect(isWorktreeLineageDropZoneHit({ pointerY: 116, rect })).toBe(true) + expect(isWorktreeLineageDropZoneHit({ pointerY: 117, rect })).toBe(false) + }) + + it.each([ + { top: 100, bottom: 100 }, + { top: 100, bottom: 90 } + ])('rejects empty and inverted rectangles', (rect) => { + expect(isWorktreeLineageDropZoneHit({ pointerY: 100, rect })).toBe(false) }) }) describe('getWorktreeLineageDropTargetId', () => { - it('returns the row id only when the pointer is in the card content middle band', () => { + it('returns the row id only when the pointer is away from the reorder gutters', () => { const { container, target } = makeTarget({ worktreeId: 'parent', top: 100, bottom: 200 }) - expect(getWorktreeLineageDropTargetId({ container, target, pointerY: 120 })).toBeNull() + expect(getWorktreeLineageDropTargetId({ container, target, pointerY: 107 })).toBeNull() expect(getWorktreeLineageDropTargetId({ container, target, pointerY: 150 })).toBe('parent') }) @@ -60,6 +76,62 @@ describe('getWorktreeLineageDropTargetId', () => { expect(getWorktreeLineageDropTargetId({ container, target, pointerY: 150 })).toBe('parent') } ) + + it('accepts horizontal card padding outside the content element', () => { + const { container } = makeTarget({ worktreeId: 'parent', top: 100, bottom: 200 }) + const row = container.firstElementChild! + const padding = document.createElement('div') + row.append(padding) + + expect(getWorktreeLineageDropTargetId({ container, target: padding, pointerY: 120 })).toBe( + 'parent' + ) + expect(getWorktreeLineageDropTargetId({ container, target: row, pointerY: 180 })).toBe('parent') + expect(getWorktreeLineageDropTargetId({ container, target: padding, pointerY: 201 })).toBeNull() + }) + + it.each([true, false])( + 'keeps descendants out of the ancestor hit zone (inline content: %s)', + (insideParentContent) => { + const { container, target } = makeTarget({ + worktreeId: 'parent', + top: 100, + bottom: insideParentContent ? 300 : 200 + }) + const child = makeTarget({ worktreeId: 'child', top: 220, bottom: 300 }) + const parentContent = target.closest('[data-worktree-card-parent-content]')! + const children = document.createElement('div') + children.append(child.container.firstElementChild!) + const childrenHost = insideParentContent ? parentContent : container.firstElementChild! + childrenHost.append(children) + const childRow = children.firstElementChild as HTMLElement + childRow.getBoundingClientRect = () => ({ top: 220, bottom: 300 }) as DOMRect + + expect( + getWorktreeLineageDropTargetId({ container, target: child.target, pointerY: 250 }) + ).toBe('child') + expect(getWorktreeLineageDropTargetId({ container, target: childRow, pointerY: 250 })).toBe( + 'child' + ) + expect( + getWorktreeLineageDropTargetId({ container, target: children, pointerY: 250 }) + ).toBeNull() + expect(getWorktreeLineageDropTargetId({ container, target, pointerY: 120 })).toBe('parent') + } + ) + + it('does not use a descendant content element for a row without its own content', () => { + const { container, target } = makeTarget({ worktreeId: 'child', top: 100, bottom: 200 }) + const parentRow = document.createElement('div') + parentRow.setAttribute('data-worktree-drag-id', 'parent') + parentRow.append(container.firstElementChild!) + container.append(parentRow) + + expect( + getWorktreeLineageDropTargetId({ container, target: parentRow, pointerY: 150 }) + ).toBeNull() + expect(getWorktreeLineageDropTargetId({ container, target, pointerY: 150 })).toBe('child') + }) }) describe('getReorderedWorktreeIdsToUnnest', () => { diff --git a/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.ts b/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.ts index a67c7ab3e43..9ef4e069dc2 100644 --- a/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.ts +++ b/src/renderer/src/components/sidebar/worktree-lineage-drag-drop.ts @@ -5,8 +5,8 @@ import { getLineageRenderInfo } from './worktree-lineage-projection' const WORKTREE_CARD_CONTENT_TARGET_SELECTOR = '[data-worktree-card-parent-content]' const WORKTREE_DRAG_ROW_SELECTOR = '[data-worktree-drag-id]' -const LINEAGE_DROP_ZONE_RATIO = 0.4 -const LINEAGE_DROP_ZONE_MAX_HEIGHT_PX = 44 +const REORDER_GUTTER_RATIO = 0.2 +const REORDER_GUTTER_MAX_HEIGHT_PX = 8 type VerticalRect = Pick @@ -19,9 +19,9 @@ export function isWorktreeLineageDropZoneHit(args: { return false } - const zoneHeight = Math.min(height * LINEAGE_DROP_ZONE_RATIO, LINEAGE_DROP_ZONE_MAX_HEIGHT_PX) - const zoneTop = args.rect.top + (height - zoneHeight) / 2 - const zoneBottom = args.rect.bottom - (height - zoneHeight) / 2 + const gutterHeight = Math.min(height * REORDER_GUTTER_RATIO, REORDER_GUTTER_MAX_HEIGHT_PX) + const zoneTop = args.rect.top + gutterHeight + const zoneBottom = args.rect.bottom - gutterHeight return args.pointerY >= zoneTop && args.pointerY <= zoneBottom } @@ -30,26 +30,31 @@ export function getWorktreeLineageDropTargetId(args: { target: Element pointerY: number }): string | null { - const contentTarget = args.target.closest(WORKTREE_CARD_CONTENT_TARGET_SELECTOR) - if (!contentTarget || !args.container.contains(contentTarget)) { + const rowTarget = args.target.closest(WORKTREE_DRAG_ROW_SELECTOR) + if (!rowTarget || !args.container.contains(rowTarget)) { return null } - // Why: nesting should be deliberate; the top/bottom of a card stays available - // for reorder drops instead of treating the whole card as a parent target. + const contentTarget = rowTarget.querySelector(WORKTREE_CARD_CONTENT_TARGET_SELECTOR) + if (!contentTarget || contentTarget.closest(WORKTREE_DRAG_ROW_SELECTOR) !== rowTarget) { + return null + } + + const rect = contentTarget.getBoundingClientRect() + // Legacy cards include descendants inside parent content; keep their rows out of its hit zone. + const firstChildRow = contentTarget.querySelector(WORKTREE_DRAG_ROW_SELECTOR) + const bottom = firstChildRow + ? Math.min(rect.bottom, firstChildRow.getBoundingClientRect().top) + : rect.bottom if ( !isWorktreeLineageDropZoneHit({ pointerY: args.pointerY, - rect: contentTarget.getBoundingClientRect() + rect: { top: rect.top, bottom } }) ) { return null } - const rowTarget = contentTarget.closest(WORKTREE_DRAG_ROW_SELECTOR) - if (!rowTarget || !args.container.contains(rowTarget)) { - return null - } return rowTarget.getAttribute('data-worktree-drag-id') } diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.test.ts b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.test.ts new file mode 100644 index 00000000000..437891d6f69 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.test.ts @@ -0,0 +1,259 @@ +// @vitest-environment happy-dom +import { act, cleanup, renderHook } from '@testing-library/react' +import { useWorktreeDragRuntime } from './use-runtime' +import { useWorktreePointerDragWindowEvents } from './use-pointer-window-events' +import { commitWorktreePointerDrop } from './pointer-commit' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { flushWorktreePointerDragFrame, type WorktreePointerDragFrameArgs } from './pointer-flush' +import { NO_WORKTREE_SIDEBAR_DROP_TARGET, WORKTREE_ROW_DRAG_INITIAL_STATE } from './row-state' + +vi.mock('../../workspace-kanban-sidebar-drop', () => ({ + clearWorkspaceKanbanSidebarDropTargetVisual: vi.fn(), + hasWorkspaceKanbanSidebarDropBoard: () => true, + isWorkspaceKanbanSidebarDropPointInBoard: () => false, + updateWorkspaceKanbanSidebarDropTargetVisual: () => ({ status: null, isPinDrop: false }) +})) + +vi.mock('./pointer-commit', () => ({ commitWorktreePointerDrop: vi.fn() })) + +afterEach(() => { + cleanup() + vi.restoreAllMocks() +}) + +function setup() { + let time = 0 + let nextFrame: FrameRequestCallback | null = null + vi.spyOn(performance, 'now').mockImplementation(() => time) + vi.spyOn(window, 'requestAnimationFrame').mockImplementation((callback) => { + nextFrame = callback + return 1 + }) + let state = WORKTREE_ROW_DRAG_INITIAL_STATE + let target = NO_WORKTREE_SIDEBAR_DROP_TARGET + const offsets = new Map([['parent', 56]]) + const args: WorktreePointerDragFrameArgs = { + drag: { + pointerId: 1, + sourceRow: document.createElement('div'), + startX: 100, + startY: 400, + currentX: 100, + currentY: 300, + worktreeId: 'child', + draggedIds: ['child'], + reorderDraggedIds: ['child'], + reorderUnitDraggedIds: ['child'], + sourceGroupKey: 'repo', + rects: [], + active: true, + preview: document.createElement('div'), + previewOffsetX: 20, + previewOffsetY: 20, + workspaceBoardDragPreviewRequested: false, + frameId: null, + reorderIntent: null, + latestBoardDropTarget: null, + latestStatusDropTarget: null + }, + ctx: { + scrollRef: { current: null }, + workspaceStatuses: [], + worktreeDragGroups: [], + worktreeDragUnitGroups: [], + refreshWorktreeDragSession: () => true, + getEligibleLineageDropTarget: () => target, + computeWorktreeDrop: () => ({ + dropIndex: 1, + dropIndicatorY: 300, + dropAnchorId: 'parent', + previewOffsetsByWorktreeId: offsets + }), + computeWorktreeStatusDrop: () => null, + commitWorktreeLineageParentDrop: () => true, + clearReorderedWorktreeParents: vi.fn(), + clearWorktreeDrag: vi.fn(), + onMoveWorktreesToStatus: vi.fn(), + onMoveWorktreesToStatusAtIndex: vi.fn(), + onReorderWorktrees: vi.fn(), + onPinWorktrees: vi.fn() + }, + workspaceBoardOpen: false, + onWorkspaceBoardDragPreviewStart: vi.fn(), + onWorkspaceBoardDragPreviewCommit: vi.fn(), + shouldShowWorkspaceBoardDropIndicator: () => false, + setDragOverStatus: vi.fn(), + setPinDragOver: vi.fn(), + setWorktreeDragState: (update) => { + state = typeof update === 'function' ? update(state) : update + } + } + return { + args, + offsets, + state: () => state, + nest: (id: string | null) => { + target = { ...NO_WORKTREE_SIDEBAR_DROP_TARGET, lineageParentId: id } + }, + tick: (ms: number) => { + time += ms + const callback = nextFrame + nextFrame = null + callback?.(time) + } + } +} + +describe('combined nesting and animated reordering', () => { + it('lets the pointer cross an edge into nesting without moving the destination', () => { + const t = setup() + flushWorktreePointerDragFrame(t.args) + expect(t.state().previewOffsetsByWorktreeId.size).toBe(0) + t.nest('parent') + t.tick(80) + expect(t.state().lineageDropTargetId).toBe('parent') + expect(t.state().previewOffsetsByWorktreeId.size).toBe(0) + t.tick(200) + expect(t.state().lineageDropTargetId).toBe('parent') + expect(t.state().dropIndicatorY).toBeNull() + }) + + it('opens the reorder gap, holds it during nesting, then restores the edge preview', () => { + const t = setup() + flushWorktreePointerDragFrame(t.args) + t.tick(160) + expect(t.state().previewOffsetsByWorktreeId).toBe(t.offsets) + expect(t.state().dropIndicatorY).toBe(300) + t.nest('parent') + flushWorktreePointerDragFrame(t.args) + expect(t.state().lineageDropTargetId).toBe('parent') + expect(t.state().previewOffsetsByWorktreeId).toBe(t.offsets) + expect(t.state().dropIndicatorY).toBeNull() + t.nest(null) + flushWorktreePointerDragFrame(t.args) + t.tick(160) + expect(t.state().lineageDropTargetId).toBeNull() + expect(t.state().previewOffsetsByWorktreeId).toBe(t.offsets) + expect(t.state().dropIndicatorY).toBe(300) + }) + + it('clears the old line during a new intent and stops scheduling after settling', () => { + const t = setup() + flushWorktreePointerDragFrame(t.args) + t.tick(160) + expect(t.state().dropIndicatorY).toBe(300) + t.args.drag.currentY = 356 + t.args.ctx.computeWorktreeDrop = () => ({ + dropIndex: 2, + dropIndicatorY: 356, + dropAnchorId: null, + previewOffsetsByWorktreeId: t.offsets + }) + flushWorktreePointerDragFrame(t.args) + expect(t.state().dropIndicatorY).toBeNull() + expect(t.state().previewOffsetsByWorktreeId).toBe(t.offsets) + t.tick(160) + expect(t.state().dropIndicatorY).toBe(356) + const frames = vi.mocked(window.requestAnimationFrame).mock.calls.length + t.tick(1000) + expect(vi.mocked(window.requestAnimationFrame).mock.calls).toHaveLength(frames) + }) +}) + +describe('stationary pointer autoscroll', () => { + it.each([1, -1])('keeps the gap tracking slots while scrolling in direction %s', (direction) => { + const t = setup() + const container = document.createElement('div') + t.args.ctx.scrollRef.current = container + let index = 10 + let offsets = new Map([['parent', 56]]) + t.args.ctx.computeWorktreeDrop = () => ({ + dropIndex: index, + dropIndicatorY: 300 - container.scrollTop, + dropAnchorId: null, + previewOffsetsByWorktreeId: offsets + }) + flushWorktreePointerDragFrame(t.args) + for (let frame = 1; frame <= 6; frame++) { + index += direction + container.scrollTop += direction * 56 + offsets = new Map([[`row-${index}`, direction * 56]]) + t.tick(80) + flushWorktreePointerDragFrame(t.args) + if (frame >= 2) { + expect(t.state().previewOffsetsByWorktreeId).toBe(offsets) + expect(t.state().dropIndicatorY).toBe(300 - container.scrollTop) + } + } + expect(t.args.drag.currentY).toBe(300) + }) +}) + +describe('Escape during pointer dragging', () => { + function renderDrag() { + const t = setup() + const cancelBoard = vi.fn() + const { result, unmount } = renderHook(() => { + const runtime = useWorktreeDragRuntime({ + worktreeDragSessionRef: { current: null }, + statusDropAnchorsRef: { current: new Map() }, + onWorkspaceBoardDragPreviewCancel: cancelBoard + }) + useWorktreePointerDragWindowEvents({ + ctx: t.args.ctx, + runtime, + beginWorktreePointerDrag: vi.fn(), + scheduleWorktreePointerDragFrame: vi.fn(), + onWorkspaceBoardDragPreviewCommit: vi.fn(), + onDropWorktreesOnWorkspaceBoard: vi.fn() + }) + return runtime + }) + result.current.worktreePointerDragRef.current = t.args.drag + if (t.args.drag.preview) { + document.body.append(t.args.drag.preview) + } + return { ...t, result, unmount, cancelBoard } + } + + it('removes the preview and cancels frames without committing on pointer release', () => { + const t = renderDrag() + const cancelFrame = vi.spyOn(window, 'cancelAnimationFrame') + t.args.drag.frameId = 12 + t.result.current.pointerAutoscrollFrameIdRef.current = 13 + const escape = new KeyboardEvent('keydown', { key: 'Escape', cancelable: true }) + act(() => window.dispatchEvent(escape)) + expect(escape.defaultPrevented).toBe(true) + expect(t.result.current.worktreePointerDragRef.current).toBeNull() + expect(t.args.drag.preview?.isConnected).toBe(false) + expect(cancelFrame).toHaveBeenCalledWith(12) + expect(cancelFrame).toHaveBeenCalledWith(13) + expect(t.cancelBoard).toHaveBeenCalledOnce() + window.dispatchEvent(new PointerEvent('pointerup', { pointerId: 1 })) + expect(commitWorktreePointerDrop).not.toHaveBeenCalled() + expect(t.result.current.worktreeDragState).toBe(WORKTREE_ROW_DRAG_INITIAL_STATE) + }) + + it('leaves other keys and Escape without a drag available to the app', () => { + const t = renderDrag() + const enter = new KeyboardEvent('keydown', { key: 'Enter', cancelable: true }) + window.dispatchEvent(enter) + expect(enter.defaultPrevented).toBe(false) + expect(t.result.current.worktreePointerDragRef.current).toBe(t.args.drag) + act(() => t.result.current.clearWorktreeDrag()) + const escape = new KeyboardEvent('keydown', { key: 'Escape', cancelable: true }) + window.dispatchEvent(escape) + expect(escape.defaultPrevented).toBe(false) + expect(t.cancelBoard).toHaveBeenCalledOnce() + }) + + it('removes its Escape listener on unmount', () => { + const t = renderDrag() + t.unmount() + const escape = new KeyboardEvent('keydown', { key: 'Escape', cancelable: true }) + window.dispatchEvent(escape) + expect(escape.defaultPrevented).toBe(false) + expect(t.cancelBoard).not.toHaveBeenCalled() + t.args.drag.preview?.remove() + }) +}) diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.ts b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.ts index 3baf8e81762..0c9017fc0f5 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.ts @@ -11,6 +11,7 @@ import { getPointerDropStatusTarget, shouldPreferSidebarStatusDropTarget } from import type { WorktreeDropCommitContext } from './drop-commit-context' import { applyWorktreeDropPreview, + applyWorktreeLineageDropPreview, clearWorktreeDropPreview, NO_WORKTREE_SIDEBAR_DROP_TARGET, updateLatestWorktreeStatusDropTarget, @@ -19,6 +20,8 @@ import { type WorktreeSidebarLineageDropTarget } from './row-state' +const REORDER_INTENT_DELAY_MS = 160 + export type WorktreePointerDragFrameArgs = { drag: WorktreePointerDrag ctx: WorktreeDropCommitContext @@ -83,6 +86,7 @@ export function flushWorktreePointerDragFrame(args: WorktreePointerDragFrameArgs if (!drag.active || !drag.preview) { return } + delete drag.preview.dataset.worktreeSidebarNesting updateSidebarDragPreviewPosition({ preview: drag.preview, pointerX: drag.currentX, @@ -121,6 +125,7 @@ export function flushWorktreePointerDragFrame(args: WorktreePointerDragFrameArgs args.onWorkspaceBoardDragPreviewCommit() } if (boardTarget.status || boardTarget.isPinDrop) { + drag.reorderIntent = null drag.latestStatusDropTarget = null clearInsertionLine(args) return @@ -137,10 +142,24 @@ export function flushWorktreePointerDragFrame(args: WorktreePointerDragFrameArgs : NO_WORKTREE_SIDEBAR_DROP_TARGET, drag.draggedIds ) - if (preferredStatusTarget.lineageParentId) { + const lineageParentId = preferredStatusTarget.lineageParentId + if (lineageParentId) { + drag.reorderIntent = null updateLatestWorktreeStatusDropTarget(drag, preferredStatusTarget, null) clearWorkspaceKanbanSidebarDropTargetVisual() - clearInsertionLine(args) + drag.preview.dataset.worktreeSidebarNesting = 'true' + updateSidebarDragPreviewPosition({ + preview: drag.preview, + pointerX: drag.currentX, + pointerY: drag.currentY, + offsetX: drag.previewOffsetX, + offsetY: drag.previewOffsetY + }) + args.setDragOverStatus(null) + args.setPinDragOver(false) + args.setWorktreeDragState((prev) => + applyWorktreeLineageDropPreview(prev, lineageParentId, drag.currentY) + ) return } if ( @@ -150,15 +169,42 @@ export function flushWorktreePointerDragFrame(args: WorktreePointerDragFrameArgs workspaceStatuses: ctx.workspaceStatuses }) ) { + drag.reorderIntent = null showStatusHoverWithoutInsertionLine(args, preferredStatusTarget) return } const drop = ctx.computeWorktreeDrop(drag.currentY) if (!drop) { + drag.reorderIntent = null showStatusHoverWithoutInsertionLine(args, preferredStatusTarget) return } + // Let the pointer cross a reorder gutter into the card before moving its target. + let intent = drag.reorderIntent + if (!intent || (intent.dropIndex !== drop.dropIndex && intent.pointerY !== drag.currentY)) { + intent = { + dropIndex: drop.dropIndex, + pointerY: drag.currentY, + startedAt: performance.now() + } + } else { + // Autoscroll changes slots beneath a stationary pointer without renewing intent. + intent.dropIndex = drop.dropIndex + intent.pointerY = drag.currentY + } + drag.reorderIntent = intent + if (performance.now() - intent.startedAt < REORDER_INTENT_DELAY_MS) { + drag.latestStatusDropTarget = null + args.setWorktreeDragState((prev) => + clearWorktreeDropPreview(prev, { + pointerY: drag.currentY, + preserveOffsets: true + }) + ) + drag.frameId = window.requestAnimationFrame(() => flushWorktreePointerDragFrame(args)) + return + } drag.latestStatusDropTarget = null clearWorkspaceKanbanSidebarDropTargetVisual() args.setDragOverStatus(null) diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/row-state.test.ts b/src/renderer/src/components/sidebar/worktree-list/drag/row-state.test.ts new file mode 100644 index 00000000000..f6b21d04667 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-list/drag/row-state.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest' +import { + applyWorktreeDropPreview, + applyWorktreeLineageDropPreview, + clearWorktreeDropPreview, + WORKTREE_ROW_DRAG_INITIAL_STATE +} from './row-state' + +const reorderPreview = { + dropAnchorId: 'parent', + dropIndex: 2, + dropIndicatorY: 180, + previewOffsetsByWorktreeId: new Map([['parent', -60]]) +} + +function reorderedState() { + return applyWorktreeDropPreview( + { ...WORKTREE_ROW_DRAG_INITIAL_STATE, draggingWorktreeId: 'child' }, + reorderPreview, + { pointerY: 150, matchPointerY: true } + ) +} + +describe('lineage drop preview', () => { + it('keeps the hovered card in place while replacing the reorder line with nesting', () => { + const before = reorderedState() + const nesting = applyWorktreeLineageDropPreview(before, 'parent', 150) + expect(nesting.previewOffsetsByWorktreeId).toBe(before.previewOffsetsByWorktreeId) + expect(nesting.dropIndex).toBeNull() + expect(nesting.dropIndicatorY).toBeNull() + expect(nesting.lineageDropTargetId).toBe('parent') + expect(applyWorktreeLineageDropPreview(nesting, 'parent', 150)).toBe(nesting) + }) + + it('repaints a target change even when the pointer stays at the same height', () => { + const before = applyWorktreeLineageDropPreview(reorderedState(), 'parent', 150) + const after = applyWorktreeLineageDropPreview(before, 'other-parent', 150) + expect(after).not.toBe(before) + expect(after.lineageDropTargetId).toBe('other-parent') + }) + + it('clears nesting feedback when returning to a reorder edge at the same height', () => { + const before = applyWorktreeLineageDropPreview(reorderedState(), 'parent', 150) + const after = applyWorktreeDropPreview(before, reorderPreview, { + pointerY: 150, + matchPointerY: true + }) + expect(after.lineageDropTargetId).toBeNull() + expect(after.dropIndicatorY).toBe(180) + }) + + it('clears nesting when leaving the sidebar even without pointer Y movement or offsets', () => { + const before = applyWorktreeLineageDropPreview(WORKTREE_ROW_DRAG_INITIAL_STATE, 'parent', 150) + const after = clearWorktreeDropPreview(before, { pointerY: 150, matchPointerY: true }) + expect(after).not.toBe(before) + expect(after.lineageDropTargetId).toBeNull() + expect(after.previewOffsetsByWorktreeId.size).toBe(0) + }) +}) diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/row-state.ts b/src/renderer/src/components/sidebar/worktree-list/drag/row-state.ts index 480f78d0a6e..1fd257df808 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/row-state.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/row-state.ts @@ -13,6 +13,7 @@ export type WorktreeRowDragState = { dropIndicatorY: number | null previewOffsetsByWorktreeId: ReadonlyMap pointerY: number | null + lineageDropTargetId: string | null } export const EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS: ReadonlyMap = new Map() @@ -23,7 +24,8 @@ export const WORKTREE_ROW_DRAG_INITIAL_STATE: WorktreeRowDragState = { dropIndex: null, dropIndicatorY: null, previewOffsetsByWorktreeId: EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, - pointerY: null + pointerY: null, + lineageDropTargetId: null } export type WorktreePointerDrag = { @@ -45,6 +47,7 @@ export type WorktreePointerDrag = { previewOffsetY: number workspaceBoardDragPreviewRequested: boolean frameId: number | null + reorderIntent: { dropIndex: number; pointerY: number; startedAt: number } | null latestBoardDropTarget: WorkspaceKanbanCardTrackedDropTarget | null latestStatusDropTarget: WorktreeSidebarTrackedStatusDropTarget | null } @@ -97,20 +100,24 @@ export function updateLatestWorktreeStatusDropTarget( // updates deliberately keep the previous state identity in that case. export function clearWorktreeDropPreview( previous: WorktreeRowDragState, - args: { pointerY: number | null; matchPointerY?: boolean } + args: { pointerY: number | null; matchPointerY?: boolean; preserveOffsets?: boolean } ): WorktreeRowDragState { const unchanged = + previous.lineageDropTargetId === null && previous.dropIndex === null && previous.dropIndicatorY === null && - previous.previewOffsetsByWorktreeId.size === 0 && + (args.preserveOffsets === true || previous.previewOffsetsByWorktreeId.size === 0) && (args.matchPointerY !== true || previous.pointerY === args.pointerY) return unchanged ? previous : { ...previous, + lineageDropTargetId: null, dropIndex: null, dropIndicatorY: null, - previewOffsetsByWorktreeId: EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, + previewOffsetsByWorktreeId: args.preserveOffsets + ? previous.previewOffsetsByWorktreeId + : EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, pointerY: args.pointerY } } @@ -121,6 +128,7 @@ export function applyWorktreeDropPreview( args: { pointerY: number; matchPointerY?: boolean } ): WorktreeRowDragState { const unchanged = + previous.lineageDropTargetId === null && previous.dropIndex === drop.dropIndex && previous.dropIndicatorY === drop.dropIndicatorY && (args.matchPointerY !== true || previous.pointerY === args.pointerY) && @@ -128,5 +136,30 @@ export function applyWorktreeDropPreview( previous.previewOffsetsByWorktreeId, drop.previewOffsetsByWorktreeId ) - return unchanged ? previous : { ...previous, ...drop, pointerY: args.pointerY } + return unchanged + ? previous + : { ...previous, ...drop, lineageDropTargetId: null, pointerY: args.pointerY } +} + +export function applyWorktreeLineageDropPreview( + previous: WorktreeRowDragState, + lineageDropTargetId: string, + pointerY: number +): WorktreeRowDragState { + if ( + previous.lineageDropTargetId === lineageDropTargetId && + previous.dropIndex === null && + previous.dropIndicatorY === null && + previous.pointerY === pointerY + ) { + return previous + } + // Keep the target under the pointer when switching from a reorder gap to nesting. + return { + ...previous, + lineageDropTargetId, + dropIndex: null, + dropIndicatorY: null, + pointerY + } } diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts index b3f7f2f54b7..e83ff97334b 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts @@ -72,6 +72,7 @@ export function useWorktreeNativeDrag(args: { setWorktreeDragState({ draggingWorktreeId: worktreeId, sourceGroupKey, + lineageDropTargetId: null, dropIndex: null, dropIndicatorY: null, previewOffsetsByWorktreeId: EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts index 7db25f797eb..1b751a18e9b 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts @@ -136,6 +136,7 @@ export function useWorktreePointerDrag(args: { setWorktreeDragState({ draggingWorktreeId: drag.worktreeId, sourceGroupKey: drag.sourceGroupKey, + lineageDropTargetId: null, dropIndex: null, dropIndicatorY: null, previewOffsetsByWorktreeId: EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, @@ -207,6 +208,7 @@ export function useWorktreePointerDrag(args: { previewOffsetY: 0, workspaceBoardDragPreviewRequested: false, frameId: null, + reorderIntent: null, latestBoardDropTarget: null, latestStatusDropTarget: null } diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-window-events.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-window-events.ts index b2989fb06ac..fa8be3a21b6 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-window-events.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-window-events.ts @@ -78,10 +78,21 @@ export function useWorktreePointerDragWindowEvents(args: { clearWorktreeDrag() } + const handleKeyDown = (event: KeyboardEvent): void => { + if (event.key !== 'Escape' || !worktreePointerDragRef.current) { + return + } + event.preventDefault() + event.stopPropagation() + clearWorktreeDrag() + } + + window.addEventListener('keydown', handleKeyDown, { capture: true }) window.addEventListener('pointermove', handlePointerMove, { capture: true }) window.addEventListener('pointerup', handlePointerUp, { capture: true }) window.addEventListener('pointercancel', handlePointerCancel, { capture: true }) return () => { + window.removeEventListener('keydown', handleKeyDown, { capture: true }) window.removeEventListener('pointermove', handlePointerMove, { capture: true }) window.removeEventListener('pointerup', handlePointerUp, { capture: true }) window.removeEventListener('pointercancel', handlePointerCancel, { capture: true }) diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx index 91ebb321726..775ecdaedb6 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx @@ -23,7 +23,7 @@ import type { LineageToggleHandler } from '../../worktree-lineage-toggle-handler import { stopNestedWorktreeCardBubble } from './header-event-guards' import type { WorktreeItemRow } from '../listing/renderable-rows' import { getWorktreeOptionId } from './option-dom' -import type { WorktreePointerDrag, WorktreeRowDragState } from '../drag/row-state' +import type { WorktreeRowDragState } from '../drag/row-state' export type WorktreeItemRowContext = { settings: AppState['settings'] @@ -33,7 +33,6 @@ export type WorktreeItemRowContext = { groupIndexByRowKey: ReadonlyMap agentSendTargetWorktreeId: string | null worktreeDragState: WorktreeRowDragState - worktreePointerDragRef: React.MutableRefObject nativeLineageDropTargetId: string | null activeWorktreeId: string | null activeWorkspaceExecutionHostId: ExecutionHostId | null @@ -139,8 +138,7 @@ export function renderWorktreeItemRow( const worktreeIdentity = getWorktreeHostIdentity(itemRow.worktree) const isLineageDropTarget = ctx.worktreeDragState.draggingWorktreeId && - (ctx.worktreePointerDragRef.current?.latestStatusDropTarget?.target.lineageParentId === - itemRow.worktree.id || + (ctx.worktreeDragState.lineageDropTargetId === itemRow.worktree.id || ctx.nativeLineageDropTargetId === itemRow.worktree.id) const isActiveWorktree = ctx.activeWorktreeId === itemRow.worktree.id && diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx index 2981021c76a..2954a2ad861 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx @@ -124,6 +124,7 @@ function renderLineageGroupVirtualRow( ref={ctx.measureVirtualRowElement} className={cn( 'absolute left-0 right-0 top-0', + parent?.worktree.id === ctx.worktreeDragState.draggingWorktreeId && 'pointer-events-none', ctx.worktreeDragState.draggingWorktreeId !== null && 'transition-transform duration-150 ease-out will-change-transform' )} @@ -234,6 +235,7 @@ export function renderWorktreeVirtualRow( data-workspace-status={itemWorkspaceStatus ?? undefined} className={cn( 'absolute left-0 right-0 top-0', + row.worktree.id === ctx.worktreeDragState.draggingWorktreeId && 'pointer-events-none', ctx.worktreeDragState.draggingWorktreeId !== null && 'transition-transform duration-150 ease-out will-change-transform' )} diff --git a/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts index 2787b93a002..ff6572b9e51 100644 --- a/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-row-context.ts @@ -117,7 +117,6 @@ export function buildWorktreeVirtualRowContext(args: BuildArgs): WorktreeVirtual groupIndexByRowKey: session.groupIndexByRowKey, agentSendTargetWorktreeId: props.agentSendTargetWorktreeId, worktreeDragState: runtime.worktreeDragState, - worktreePointerDragRef: runtime.worktreePointerDragRef, nativeLineageDropTargetId: runtime.nativeLineageDropTargetId, activeWorktreeId: props.activeWorktreeId, activeWorkspaceExecutionHostId: props.activeWorkspaceExecutionHostId, diff --git a/src/renderer/src/components/sidebar/worktree-sidebar-pointer-drag-dom.ts b/src/renderer/src/components/sidebar/worktree-sidebar-pointer-drag-dom.ts index 54f302f872b..1aa2f523311 100644 --- a/src/renderer/src/components/sidebar/worktree-sidebar-pointer-drag-dom.ts +++ b/src/renderer/src/components/sidebar/worktree-sidebar-pointer-drag-dom.ts @@ -59,8 +59,10 @@ export function updateSidebarDragPreviewPosition(args: { offsetX: number offsetY: number }): void { - const x = args.pointerX - args.offsetX - const y = args.pointerY - args.offsetY + const nesting = args.preview.hasAttribute('data-worktree-sidebar-nesting') + // Keep the destination title visible while the pointer rests inside it. + const x = nesting ? args.pointerX + 16 : args.pointerX - args.offsetX + const y = nesting ? args.pointerY + 16 : args.pointerY - args.offsetY args.preview.style.transform = `translate3d(${x}px, ${y}px, 0) scale(1.015)` } diff --git a/src/renderer/src/components/skills/skill-agent-filter.test.ts b/src/renderer/src/components/skills/skill-agent-filter.test.ts new file mode 100644 index 00000000000..da59a1002a6 --- /dev/null +++ b/src/renderer/src/components/skills/skill-agent-filter.test.ts @@ -0,0 +1,203 @@ +import { describe, expect, it } from 'vitest' +import type { DiscoveredSkill } from '../../../../shared/skills' +import { skillAgentOptions, skillMatchesAgent } from './skill-agent-filter' + +function skill(overrides: Partial = {}): DiscoveredSkill { + return { + id: 'id', + name: 'Review', + description: 'Code review', + providers: ['agent-skills'], + sourceKind: 'home', + sourceLabel: 'Agent skills home', + rootPath: '/a', + directoryPath: '/a/review', + skillFilePath: '/a/review/SKILL.md', + installed: true, + updatedAt: null, + ...overrides + } +} + +/** Pre-change implementation, kept verbatim as the parity oracle. */ +function legacySkillMatchesAgent( + target: DiscoveredSkill, + agentId: string, + agentByRootPath: ReadonlyMap +): boolean { + const agents = (): string[] => { + const roots = target.rootPaths?.length ? target.rootPaths : [target.rootPath] + return [...new Set(roots.map((root) => agentByRootPath.get(root)).filter(Boolean))] as string[] + } + return agentId === 'all' || agents().includes(agentId) +} + +/** Counts ownership lookups so the before/after difference is deterministic. */ +class LookupCountingMap extends Map { + lookups = 0 + override get(key: string): string | undefined { + this.lookups += 1 + return super.get(key) + } +} + +const WINDOWS_ROOT = 'C:\\Users\\dev\\.agents\\skills' +const OPAQUE_REMOTE_ROOT = 'orca-ssh://build-box/srv/shared/.agents/skills' + +const OWNERS_WITH_EMPTY: [string, string][] = [ + ['/a', 'claude'], + ['/b', 'codex'], + ['/c', 'codex'], + ['/empty', ''], + [WINDOWS_ROOT, 'shared'], + [OPAQUE_REMOTE_ROOT, 'claude'] +] +const OWNERS_WITHOUT_EMPTY: [string, string][] = OWNERS_WITH_EMPTY.filter( + ([, owner]) => owner !== '' +).map(([root, owner]) => (root === '/a' ? [root, 'shared'] : [root, owner])) + +describe('skillMatchesAgent', () => { + it('matches the legacy agent list across sparse, repeated, and opaque roots', () => { + const rootPathsCases: (string[] | undefined)[] = [ + undefined, + [], + ['/a'], + ['/a', '/a'], + ['/a', '/b'], + ['/b', '/a'], + ['/missing'], + ['/a', '/missing'], + ['/empty'], + ['/a', '/empty'], + [WINDOWS_ROOT], + [OPAQUE_REMOTE_ROOT] + ] + const rootPathCases = ['/a', '/missing', '/empty', WINDOWS_ROOT, OPAQUE_REMOTE_ROOT] + const agentIds = ['all', 'claude', 'codex', 'shared', '', 'unknown'] + const maps = [new Map(OWNERS_WITH_EMPTY), new Map(OWNERS_WITHOUT_EMPTY)] + + let cases = 0 + for (const rootPaths of rootPathsCases) { + for (const rootPath of rootPathCases) { + for (const agentId of agentIds) { + for (const agentByRootPath of maps) { + const row = skill({ rootPath, rootPaths }) + expect({ + rootPaths, + rootPath, + agentId, + matched: skillMatchesAgent(row, agentId, agentByRootPath) + }).toEqual({ + rootPaths, + rootPath, + agentId, + matched: legacySkillMatchesAgent(row, agentId, agentByRootPath) + }) + cases += 1 + } + } + } + } + expect(cases).toBe(720) + }) + + it('treats populated rootPaths as authoritative instead of unioning rootPath', () => { + const agentByRootPath = new Map(OWNERS_WITH_EMPTY) + const row = skill({ rootPath: '/a', rootPaths: ['/b'] }) + expect(skillMatchesAgent(row, 'claude', agentByRootPath)).toBe(false) + expect(skillMatchesAgent(row, 'codex', agentByRootPath)).toBe(true) + }) + + it('falls back to rootPath only when rootPaths is absent or empty', () => { + const agentByRootPath = new Map(OWNERS_WITH_EMPTY) + expect(skillMatchesAgent(skill({ rootPaths: undefined }), 'claude', agentByRootPath)).toBe(true) + expect(skillMatchesAgent(skill({ rootPaths: [] }), 'claude', agentByRootPath)).toBe(true) + }) + + it('never lets an empty owner become a filter', () => { + const agentByRootPath = new Map(OWNERS_WITH_EMPTY) + expect(skillMatchesAgent(skill({ rootPath: '/empty' }), '', agentByRootPath)).toBe(false) + expect(skillMatchesAgent(skill({ rootPaths: ['/empty'] }), '', agentByRootPath)).toBe(false) + expect(skillMatchesAgent(skill({ rootPath: '/a' }), '', agentByRootPath)).toBe(false) + }) + + it('keeps "all" a success before any ownership lookup', () => { + const agentByRootPath = new LookupCountingMap(OWNERS_WITH_EMPTY) + expect( + skillMatchesAgent(skill({ rootPaths: ['/a', '/b', '/c'] }), 'all', agentByRootPath) + ).toBe(true) + expect(agentByRootPath.lookups).toBe(0) + }) + + it('cuts ownership lookups from 30,000 to 10,000 for 10,000 three-root rows', () => { + const rows = Array.from({ length: 10_000 }, (_, index) => + skill({ id: `skill-${index}`, rootPaths: ['/a', '/b', '/c'] }) + ) + const before = new LookupCountingMap(OWNERS_WITH_EMPTY) + const after = new LookupCountingMap(OWNERS_WITH_EMPTY) + + const legacyMatches = rows.map((row) => legacySkillMatchesAgent(row, 'claude', before)) + const matches = rows.map((row) => skillMatchesAgent(row, 'claude', after)) + + expect(matches).toEqual(legacyMatches) + expect(matches.every(Boolean)).toBe(true) + expect(before.lookups).toBe(30_000) + expect(after.lookups).toBe(10_000) + }) + + it('still scans every root when none of them owns the filtered agent', () => { + const agentByRootPath = new LookupCountingMap(OWNERS_WITH_EMPTY) + expect( + skillMatchesAgent(skill({ rootPaths: ['/a', '/b', '/c'] }), 'unknown', agentByRootPath) + ).toBe(false) + expect(agentByRootPath.lookups).toBe(3) + }) +}) + +describe('skillAgentOptions', () => { + it('still counts every owning root once per skill', () => { + const options = skillAgentOptions({ + scannedAt: 1, + sources: [ + { + id: 'a', + label: 'A', + path: '/a', + sourceKind: 'home', + providers: [], + owner: 'claude', + exists: true + }, + { + id: 'b', + label: 'B', + path: '/b', + sourceKind: 'repo', + providers: [], + owner: 'codex', + exists: true + }, + { + id: 'shared', + label: 'S', + path: '/s', + sourceKind: 'repo', + providers: [], + owner: null, + exists: true + } + ], + skills: [ + skill({ id: 'one', rootPaths: ['/a', '/a', '/b'] }), + skill({ id: 'two', rootPaths: ['/a'] }), + skill({ id: 'three', rootPath: '/s', rootPaths: undefined }), + skill({ id: 'four', rootPath: '/missing', rootPaths: undefined }) + ] + }) + expect(options.map((option) => [option.id, option.count])).toEqual([ + ['claude', 2], + ['codex', 1], + ['shared', 1] + ]) + }) +}) diff --git a/src/renderer/src/components/skills/skill-agent-filter.ts b/src/renderer/src/components/skills/skill-agent-filter.ts index 78c35057137..bb497206f13 100644 --- a/src/renderer/src/components/skills/skill-agent-filter.ts +++ b/src/renderer/src/components/skills/skill-agent-filter.ts @@ -35,12 +35,22 @@ function skillAgents( return [...new Set(roots.map((root) => agentByRootPath.get(root)).filter(Boolean))] as string[] } +/** Membership only, so it stops at the first owning root instead of materializing + * the deduplicated agent list `skillAgentOptions` still needs for counting. */ export function skillMatchesAgent( skill: DiscoveredSkill, agentId: string, agentByRootPath: ReadonlyMap ): boolean { - return agentId === 'all' || skillAgents(skill, agentByRootPath).includes(agentId) + if (agentId === 'all') { + return true + } + if (!agentId) { + return false // An empty owner is never a filter; parity with the dropped `.filter(Boolean)`. + } + return skill.rootPaths?.length + ? skill.rootPaths.some((root) => agentByRootPath.get(root) === agentId) + : agentByRootPath.get(skill.rootPath) === agentId } /** Only agents that actually hold a skill; an empty root is not a filter. */ diff --git a/src/renderer/src/components/skills/skill-source-inventory.test.ts b/src/renderer/src/components/skills/skill-source-inventory.test.ts index 67fd41965e8..1d863ea3f6b 100644 --- a/src/renderer/src/components/skills/skill-source-inventory.test.ts +++ b/src/renderer/src/components/skills/skill-source-inventory.test.ts @@ -41,6 +41,61 @@ function result(overrides: Partial = {}): SkillDiscoveryRe } describe('summarizeSkillSources', () => { + it('counts each skill once per root, including a primary root omitted from rootPaths', () => { + const shared = skill({ rootPaths: ['/other', '/other', '/unknown'] }) + const home = source() + const other = source({ path: '/other' }) + const entries = summarizeSkillSources( + result({ + sources: [home, other, other, source({ path: '/OTHER' })], + skills: [shared, shared, skill({ rootPaths: [home.path, home.path] })] + }) + ) + expect(entries.map((entry) => entry.skillCount)).toEqual([3, 2, 2, 0]) + expect(entries[0].source).toBe(home) + expect(entries[1].source).toBe(other) + expect(entries[2].source).toBe(other) + }) + + it('does not scan every skill again for each source', () => { + let rootReads = 0 + const skills = Array.from({ length: 1000 }, () => ({ + ...skill(), + get rootPath() { + rootReads++ + return '/home/dev/.agents/skills' + } + })) + const sources = Array.from({ length: 87 }, (_, index) => source({ id: `${index}` })) + const entries = summarizeSkillSources(result({ skills, sources })) + expect(entries.every((entry) => entry.skillCount === 1000)).toBe(true) + expect(rootReads).toBeLessThanOrEqual(skills.length) + }) + + it('does not inspect skills without sources', () => { + const unused = { + ...skill(), + get rootPath(): string { + throw new Error('No source needs a count') + } + } + expect(summarizeSkillSources(null)).toEqual([]) + expect(summarizeSkillSources(result({ skills: [unused] }))).toEqual([]) + }) + + it('accepts frozen ownership lists and inputs without changing them', () => { + const home = Object.freeze(source()) + const item = skill({ rootPaths: [home.path, '/co-owner', home.path] }) + Object.freeze(item.rootPaths) + Object.freeze(item) + const discovery = result({ sources: [home, source({ path: '/co-owner' })], skills: [item] }) + Object.freeze(discovery.sources) + Object.freeze(discovery.skills) + Object.freeze(discovery) + expect(summarizeSkillSources(discovery).map((entry) => entry.skillCount)).toEqual([1, 1]) + expect(item.rootPaths).toEqual([home.path, '/co-owner', home.path]) + }) + it('counts a symlinked skill under every root that reached it', () => { const shared = source({ id: 'repo', path: '/repo/.agents/skills', sourceKind: 'repo' }) const entries = summarizeSkillSources( diff --git a/src/renderer/src/components/skills/skill-source-inventory.ts b/src/renderer/src/components/skills/skill-source-inventory.ts index 1bedd5fa02b..708334530f1 100644 --- a/src/renderer/src/components/skills/skill-source-inventory.ts +++ b/src/renderer/src/components/skills/skill-source-inventory.ts @@ -13,8 +13,6 @@ export type SkillSourceInventoryEntry = { } function ownsSkill(source: SkillDiscoverySource, skill: DiscoveredSkill): boolean { - // Why: a symlinked skill is deduped to one row but keeps every root that - // reached it, so counting only `rootPath` would zero out the co-owning roots. return skill.rootPath === source.path || (skill.rootPaths?.includes(source.path) ?? false) } @@ -37,12 +35,38 @@ function sourceStatus(source: SkillDiscoverySource): SkillSourceStatus { export function summarizeSkillSources( result: SkillDiscoveryResult | null ): SkillSourceInventoryEntry[] { - if (!result) { + if (!result || result.sources.length === 0) { return [] } + // With no repeated skill traversal, the direct count needs no index. + if (result.sources.length === 1 || result.skills.length === 0) { + return result.sources.map((source) => ({ + source, + skillCount: result.skills.filter((skill) => ownsSkill(source, skill)).length, + status: sourceStatus(source) + })) + } + const counts = new Map( + result.sources.map((source) => [source.path, { count: 0, lastSkillIndex: -1 }]) + ) + const countRoot = (rootPath: string, skillIndex: number): void => { + const count = counts.get(rootPath) + // Symlinked skills can name one owning root more than once. + if (count && count.lastSkillIndex !== skillIndex) { + count.count++ + count.lastSkillIndex = skillIndex + } + } + for (let index = 0; index < result.skills.length; index++) { + const skill = result.skills[index] + countRoot(skill.rootPath, index) + for (const rootPath of skill.rootPaths ?? []) { + countRoot(rootPath, index) + } + } return result.sources.map((source) => ({ source, - skillCount: result.skills.filter((skill) => ownsSkill(source, skill)).length, + skillCount: counts.get(source.path)?.count ?? 0, status: sourceStatus(source) })) } diff --git a/src/renderer/src/components/status-bar/UpdateStatusSegment.test.tsx b/src/renderer/src/components/status-bar/UpdateStatusSegment.test.tsx new file mode 100644 index 00000000000..085795246cf --- /dev/null +++ b/src/renderer/src/components/status-bar/UpdateStatusSegment.test.tsx @@ -0,0 +1,156 @@ +// @vitest-environment happy-dom +import { act, cleanup, fireEvent, render, screen } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { UpdateStatus } from '../../../../shared/update-status-types' +import { useAppStore } from '../../store' +import { UpdateCard } from '../UpdateCard' +import { TooltipProvider } from '../ui/tooltip' +import { UpdateStatusSegment } from './UpdateStatusSegment' + +const check = vi.fn() +const message = 'Could not reach the update server: net::ERR_CONNECTION_REFUSED' +const error: UpdateStatus = { state: 'error', message } +const actionableErrors: UpdateStatus[] = [ + { ...error, userInitiated: true }, + { ...error, version: '1.4.200' }, + { + ...error, + recovery: { + kind: 'linux-package-install', + packageType: 'deb', + reason: 'manual-install-required', + version: '1.4.200' + } + } +] + +function setStatus(status: UpdateStatus): void { + act(() => useAppStore.getState().setUpdateStatus(status)) +} + +function renderUpdateControls(): void { + render( + + + + + ) +} + +function errorToggle(): HTMLElement { + return screen.getByRole('button', { name: 'Update failed. Click to expand.' }) +} + +beforeEach(() => { + useAppStore.setState(useAppStore.getInitialState(), true) + check.mockReset().mockResolvedValue(undefined) + vi.stubGlobal( + 'matchMedia', + vi.fn().mockReturnValue({ + matches: true, + addEventListener: vi.fn(), + removeEventListener: vi.fn() + }) + ) + Object.defineProperty(window, 'api', { + configurable: true, + value: { updater: { check } } + }) +}) + +afterEach(() => { + cleanup() + useAppStore.setState(useAppStore.getInitialState(), true) + vi.unstubAllGlobals() +}) + +describe('update status disclosure', () => { + it('opens background check failure details on the first click and offers a re-check', () => { + renderUpdateControls() + setStatus({ state: 'checking' }) + setStatus(error) + + expect(screen.queryByRole('complementary', { name: 'Update error' })).toBeNull() + fireEvent.click(errorToggle()) + + expect(screen.getByRole('complementary', { name: 'Update error' })).toBeTruthy() + expect(errorToggle().getAttribute('aria-expanded')).toBe('true') + fireEvent.click(screen.getByRole('button', { name: 'Show details' })) + expect(screen.getByText(message)).toBeTruthy() + fireEvent.click(screen.getByRole('button', { name: 'Re-check' })) + expect(check).toHaveBeenCalledWith({ includePrerelease: false }) + + fireEvent.click(errorToggle()) + expect(screen.queryByRole('complementary', { name: 'Update error' })).toBeNull() + expect(errorToggle().getAttribute('aria-expanded')).toBe('false') + }) + + it('announces a quiet automatic check failure as collapsed', () => { + setStatus(error) + renderUpdateControls() + + expect(screen.queryByRole('complementary', { name: 'Update error' })).toBeNull() + expect(errorToggle().getAttribute('aria-expanded')).toBe('false') + }) + + it('preserves the disclosure choice until the next automatic check', () => { + renderUpdateControls() + setStatus(error) + fireEvent.click(errorToggle()) + setStatus({ ...error, message: 'Still unavailable' }) + expect(screen.getByRole('complementary', { name: 'Update error' })).toBeTruthy() + + fireEvent.click(errorToggle()) + setStatus(error) + expect(screen.queryByRole('complementary', { name: 'Update error' })).toBeNull() + fireEvent.click(errorToggle()) + + setStatus({ state: 'checking' }) + setStatus(error) + expect(screen.queryByRole('complementary', { name: 'Update error' })).toBeNull() + expect(errorToggle().getAttribute('aria-expanded')).toBe('false') + }) + + it.each([ + { state: 'checking', userInitiated: true }, + { state: 'available', version: '1.4.200', changelog: null }, + { state: 'downloading', version: '1.4.200', percent: 50 }, + { state: 'downloaded', version: '1.4.200' } + ])('opens an error after $state without requiring a status click', (previousStatus) => { + setStatus(previousStatus) + setStatus(error) + renderUpdateControls() + + expect(screen.getByRole('complementary', { name: 'Update error' })).toBeTruthy() + expect(errorToggle().getAttribute('aria-expanded')).toBe('true') + }) + + it.each(actionableErrors)( + 'opens an explicit actionable error on initial receipt: %j', + (status) => { + setStatus(status) + renderUpdateControls() + + expect(screen.getByRole('complementary', { name: 'Update error' })).toBeTruthy() + expect( + screen.getByRole('button', { name: /Click to expand/ }).getAttribute('aria-expanded') + ).toBe('true') + } + ) + + it.each(actionableErrors)( + 'opens a newly actionable error and preserves dismissal on repeat: %j', + (status) => { + renderUpdateControls() + setStatus(error) + expect(screen.queryByRole('complementary', { name: 'Update error' })).toBeNull() + + setStatus(status) + expect(screen.getByRole('complementary', { name: 'Update error' })).toBeTruthy() + + fireEvent.click(screen.getByRole('button', { name: /Click to expand/ })) + setStatus({ ...status }) + expect(screen.queryByRole('complementary', { name: 'Update error' })).toBeNull() + } + ) +}) diff --git a/src/renderer/src/components/status-bar/mergeSnapshotAndSessions.ts b/src/renderer/src/components/status-bar/mergeSnapshotAndSessions.ts index 7e5dd27b6bb..5cd992e2b60 100644 --- a/src/renderer/src/components/status-bar/mergeSnapshotAndSessions.ts +++ b/src/renderer/src/components/status-bar/mergeSnapshotAndSessions.ts @@ -39,13 +39,13 @@ import { buildResourceSessionBindingIndex, type ResourceSessionBindingIndex } from './resource-session-bindings' +import { + resolveResourceFolderWorkspace, + resolveResourceWorkspaceHost +} from './resource-workspace-host' // ─── Helpers ──────────────────────────────────────────────────────── -function deriveRepoIdFromWorktreeId(worktreeId: string): string { - return getRepoIdFromWorktreeId(worktreeId) -} - function deriveWorktreeNameFromWorktreeId(worktreeId: string): string { return getWorktreePathBasenameFromId(worktreeId) ?? worktreeId } @@ -156,24 +156,7 @@ export function mergeSnapshotAndSessions( daemonSessions.map((session) => [session.id, session.agentOwnership]) ) - function isRepoRemote(repoId: string): boolean { - // Why: missing entry === we don't know about this repo (typically the - // unattributed bucket or a session whose repo metadata never made it - // into the renderer). Treat unknown as not-remote so a missing-data - // edge case can never spuriously flip the chip on. The chip should - // only fire when we have positive evidence the repo is SSH-backed. - return ctx.repoConnectionIdById.get(repoId) != null - } - - function isRuntimeScopedRepo(repoId: string): boolean { - return ctx.repoRuntimeScopedById.get(repoId) === true - } - - function ensureRepo( - repoId: string, - repoName: string, - initiallyHasRemoteChildren = false - ): UnifiedProjectGroup { + function ensureRepo(repoId: string, repoName: string): UnifiedProjectGroup { const existing = repos.get(repoId) if (existing) { return existing @@ -183,7 +166,7 @@ export function mergeSnapshotAndSessions( repoName, cpu: null, memory: null, - hasRemoteChildren: initiallyHasRemoteChildren || isRepoRemote(repoId), + hasRemoteChildren: false, worktrees: [] } repos.set(repoId, next) @@ -200,6 +183,7 @@ export function mergeSnapshotAndSessions( function appendWorktreeRow(repo: UnifiedProjectGroup, row: UnifiedWorktreeRow): void { repo.worktrees.push(row) + repo.hasRemoteChildren ||= row.isRemote const rows = worktreeRowsByRepo.get(repo.repoId)! if (!rows.has(row.worktreeId)) { rows.set(row.worktreeId, row) @@ -209,12 +193,16 @@ export function mergeSnapshotAndSessions( // ── Step 1: ingest snapshot worktrees as the local-truth foundation. if (snapshot) { for (const wt of snapshot.worktrees as readonly WorktreeMemory[]) { + const worktree = resolveResourceFolderWorkspace(ctx, wt.worktreeId) + const repoId = worktree?.repoId ?? wt.repoId + const repoName = (worktree && ctx.repoDisplayNameById.get(repoId)) || wt.repoName + const { isRemote, isRuntimeScoped } = resolveResourceWorkspaceHost(ctx, wt.worktreeId, repoId) // Why: local snapshot data must never render under a runtime-hosted repo // row; belt-and-braces with the matching session-ingest guard below. - if (isRuntimeScopedRepo(wt.repoId)) { + if (isRuntimeScoped) { continue } - const repo = ensureRepo(wt.repoId, wt.repoName) + const repo = ensureRepo(repoId, repoName) const sessions: UnifiedSessionRow[] = wt.sessions.map((s) => { seenSessionIds.add(s.sessionId) const tabId = index.ptyIdToTabId.get(s.sessionId) ?? null @@ -233,14 +221,14 @@ export function mergeSnapshotAndSessions( }) appendWorktreeRow(repo, { worktreeId: wt.worktreeId, - worktreeName: wt.worktreeName, - repoId: wt.repoId, - repoName: wt.repoName, + worktreeName: worktree?.displayName?.trim() || wt.worktreeName, + repoId, + repoName, cpu: wt.cpu, memory: wt.memory, history: wt.history, hasLocalSamples: true, - isRemote: isRepoRemote(wt.repoId), + isRemote, sessions, browsers: [] }) @@ -258,35 +246,37 @@ export function mergeSnapshotAndSessions( const tabId = index.ptyIdToTabId.get(session.id) ?? null let worktreeId = tabId ? (index.tabIdToWorktreeId.get(tabId) ?? null) : null - // 2b: @@-parse — recover worktreeId from the minted session id format. + // Prefer daemon metadata; older publishers may only encode the workspace in the session id. if (!worktreeId) { - worktreeId = parsePtySessionId(session.id).worktreeId + worktreeId = session.worktreeId || parsePtySessionId(session.id).worktreeId } // 2c: unattributed bucket. const isUnattributed = !worktreeId const finalWorktreeId = worktreeId ?? `${UNATTRIBUTED_REPO_ID}::${session.id}` + const worktree = resolveResourceFolderWorkspace(ctx, finalWorktreeId) const finalRepoId = isUnattributed ? UNATTRIBUTED_REPO_ID - : deriveRepoIdFromWorktreeId(finalWorktreeId) + : (worktree?.repoId ?? getRepoIdFromWorktreeId(finalWorktreeId)) const finalRepoName = isUnattributed ? UNATTRIBUTED_REPO_NAME : ctx.repoDisplayNameById.get(finalRepoId) || finalRepoId const finalWorktreeName = isUnattributed ? session.title || session.id.slice(0, 12) - : deriveWorktreeNameFromWorktreeId(finalWorktreeId) + : worktree?.displayName?.trim() || deriveWorktreeNameFromWorktreeId(finalWorktreeId) // Why: the current daemon inputs are local/SSH only; this guard prevents a // future local daemon row accidentally exposing kill actions for runtime PTYs. - if (isRuntimeScopedRepo(finalRepoId)) { + const { isRemote, isRuntimeScoped } = resolveResourceWorkspaceHost( + ctx, + finalWorktreeId, + finalRepoId + ) + if (isRuntimeScoped) { continue } - const repoIsRemote = isRepoRemote(finalRepoId) - const repo = ensureRepo(finalRepoId, finalRepoName, repoIsRemote) - if (repoIsRemote) { - repo.hasRemoteChildren = true - } + const repo = ensureRepo(finalRepoId, finalRepoName) let row = findWorktreeRow(repo, finalWorktreeId) if (!row) { @@ -299,7 +289,7 @@ export function mergeSnapshotAndSessions( memory: null, history: [], hasLocalSamples: false, - isRemote: repoIsRemote, + isRemote, sessions: [], browsers: [] } @@ -339,7 +329,7 @@ export function mergeSnapshotAndSessions( memory: null, history: [], hasLocalSamples: false, - isRemote: isRepoRemote(worktree.repoId), + isRemote: resolveResourceWorkspaceHost(ctx, worktreeId, worktree.repoId).isRemote, sessions: [], browsers: [] } @@ -348,10 +338,7 @@ export function mergeSnapshotAndSessions( row.browsers = browsers } - // ── Step 4: per-repo aggregates. Remote children are identified by the - // repo's connectionId, not by missing data — `!hasLocalSamples` would - // mislabel warm-reattached local PTYs. The aggregate still skips rows - // we can't sample (worktree.cpu === null) so the numbers stay honest. + // Only sampled rows contribute to project totals. for (const repo of repos.values()) { let cpuSum = 0 let memSum = 0 diff --git a/src/renderer/src/components/status-bar/resource-session-classification-parity.test.ts b/src/renderer/src/components/status-bar/resource-session-classification-parity.test.ts index fafe3cc155b..ec7c7a62848 100644 --- a/src/renderer/src/components/status-bar/resource-session-classification-parity.test.ts +++ b/src/renderer/src/components/status-bar/resource-session-classification-parity.test.ts @@ -16,7 +16,7 @@ describe('resource session classification parity', () => { const source = readFileSync(DERIVED_MODEL_PATH, 'utf8') const mergeCall = source.slice( source.indexOf('mergeSnapshotAndSessions(resourceSnapshot'), - source.indexOf('worktreeById\n })') + source.indexOf('ambiguousWorktreeIds\n })') ) expect(mergeCall).toContain('...resourceSessionBindings') diff --git a/src/renderer/src/components/status-bar/resource-usage-folder-merge.test.ts b/src/renderer/src/components/status-bar/resource-usage-folder-merge.test.ts new file mode 100644 index 00000000000..6dc264bc975 --- /dev/null +++ b/src/renderer/src/components/status-bar/resource-usage-folder-merge.test.ts @@ -0,0 +1,160 @@ +import { describe, expect, it } from 'vitest' +import type { MemorySnapshot, WorktreeMemory } from '../../../../shared/process-stats-types' +import type { Worktree } from '../../../../shared/worktree/types' +import type { MergeContext } from './resource-usage-merge-types' +import { mergeSnapshotAndSessions } from './mergeSnapshotAndSessions' + +const workspace = { + id: 'folder:notes', + repoId: 'folder-workspace:docs', + displayName: 'Release notes' +} as Worktree +const secondWorkspace = { ...workspace, id: 'folder:research', displayName: 'Research' } +const oldRow: WorktreeMemory = { + worktreeId: workspace.id, + worktreeName: workspace.id, + repoId: workspace.id, + repoName: workspace.id, + cpu: 2, + memory: 2048, + history: [1024, 2048], + sessions: [{ sessionId: 'sampled', paneKey: null, pid: 123, cpu: 2, memory: 2048 }] +} + +function context(overrides: Partial = {}): MergeContext { + return { + tabsByWorktree: {}, + ptyIdsByTabId: {}, + runtimePaneTitlesByTabId: {}, + workspaceSessionReady: true, + repoDisplayNameById: new Map([[workspace.repoId, 'Documentation']]), + repoConnectionIdById: new Map(), + repoRuntimeScopedById: new Map(), + worktreeById: new Map([ + [workspace.id, workspace], + [secondWorkspace.id, secondWorkspace] + ]), + ...overrides + } +} + +describe('folder Resource Manager rows', () => { + it('names daemon-only folders using the host-reported workspace identity', () => { + const groups = mergeSnapshotAndSessions( + null, + [ + { + id: 'folder:notes@@session', + worktreeId: workspace.id, + cwd: '/notes', + title: 'Shell', + agentOwnership: 'unknown' + } + ], + context() + ) + expect(groups).toHaveLength(1) + expect(groups[0]).toMatchObject({ + repoId: workspace.repoId, + repoName: 'Documentation', + cpu: null, + memory: null, + worktrees: [ + { + worktreeId: workspace.id, + worktreeName: 'Release notes', + hasLocalSamples: false, + isRemote: false, + sessions: [{ bound: false, agentOwnership: 'unknown' }] + } + ] + }) + }) + + it('merges old snapshot labels and daemon-only rows into the same named folder without losing metrics', () => { + const groups = mergeSnapshotAndSessions( + { worktrees: [oldRow] } as MemorySnapshot, + [ + { + id: 'extra', + worktreeId: workspace.id, + cwd: '/notes', + title: 'Shell', + agentOwnership: 'present' + }, + { + id: 'second', + worktreeId: secondWorkspace.id, + cwd: '/research', + title: 'Shell', + agentOwnership: 'unknown' + } + ], + context() + ) + expect(groups).toHaveLength(1) + expect(groups[0]).toMatchObject({ repoName: 'Documentation', cpu: 2, memory: 2048 }) + expect(groups[0].worktrees).toHaveLength(2) + expect(groups[0].worktrees[0]).toMatchObject({ + worktreeName: 'Release notes', + cpu: 2, + memory: 2048, + history: [1024, 2048] + }) + expect(groups[0].worktrees[0].sessions.map((s) => s.sessionId)).toEqual(['sampled', 'extra']) + expect(groups[0].worktrees[1].worktreeName).toBe('Research') + }) + + it('keeps old snapshot labels when the folder catalog is unavailable', () => { + const groups = mergeSnapshotAndSessions( + { worktrees: [oldRow] } as MemorySnapshot, + [], + context({ worktreeById: new Map() }) + ) + expect(groups[0].worktrees[0]).toMatchObject({ + worktreeId: workspace.id, + worktreeName: workspace.id, + memory: 2048 + }) + }) + + it('excludes runtime-owned folder sessions from local snapshot and daemon inputs', () => { + expect( + mergeSnapshotAndSessions( + { worktrees: [oldRow] } as MemorySnapshot, + [ + { + id: 'foreign', + worktreeId: workspace.id, + cwd: '/notes', + title: 'Shell', + agentOwnership: 'present' + } + ], + context({ + worktreeById: new Map([[workspace.id, { ...workspace, hostId: 'runtime:paired' }]]) + }) + ) + ).toEqual([]) + }) + + it('keeps an SSH folder marked remote even without a numeric sample', () => { + const groups = mergeSnapshotAndSessions( + null, + [ + { + id: 'ssh-folder', + worktreeId: workspace.id, + cwd: '/notes', + title: 'Shell', + agentOwnership: 'present' + } + ], + context({ + worktreeById: new Map([[workspace.id, { ...workspace, hostId: 'ssh:ssh-target' }]]) + }) + ) + expect(groups[0]).toMatchObject({ hasRemoteChildren: true, memory: null }) + expect(groups[0].worktrees[0]).toMatchObject({ isRemote: true, memory: null }) + }) +}) diff --git a/src/renderer/src/components/status-bar/resource-usage-merge-types.ts b/src/renderer/src/components/status-bar/resource-usage-merge-types.ts index 5943b744dfd..a67f5e0ec66 100644 --- a/src/renderer/src/components/status-bar/resource-usage-merge-types.ts +++ b/src/renderer/src/components/status-bar/resource-usage-merge-types.ts @@ -35,7 +35,7 @@ export type UnifiedWorktreeRow = { memory: Metric history: number[] hasLocalSamples: boolean - /** Why: repo connectionId, not sample presence, drives the remote chip. */ + /** Execution-host metadata drives the remote chip; missing samples do not. */ isRemote: boolean sessions: UnifiedSessionRow[] browsers: BrowserWorkspace[] @@ -46,7 +46,7 @@ export type UnifiedProjectGroup = { repoName: string cpu: Metric memory: Metric - /** Why: kept for callsite stability; this now means SSH-backed repo rows. */ + /** True when any workspace in this project runs over SSH. */ hasRemoteChildren: boolean worktrees: UnifiedWorktreeRow[] } @@ -64,7 +64,7 @@ export type MergeContext = { runtimePaneTitlesByTabId: Record> /** From useAppStore: false until renderer state can distinguish bound/orphan. */ workspaceSessionReady: boolean - /** Repo display names by repo id for daemon-only groups. */ + /** Project display names for sampled and daemon-only groups. */ repoDisplayNameById: Map /** Repo connectionId by repo id (null/missing == local). */ repoConnectionIdById: Map @@ -72,6 +72,8 @@ export type MergeContext = { repoRuntimeScopedById: Map /** Browser inventory is open-only; the Resource Manager never scans it in the background. */ browserTabsByWorktree?: Record - /** Canonical worktrees keep browser-only workspace rows out of synthetic buckets. */ + /** Canonical workspace names and grouping for every resource source. */ worktreeById?: ReadonlyMap + /** Ids present on more than one execution host; their catalog row cannot name a host. */ + ambiguousWorktreeIds?: ReadonlySet } diff --git a/src/renderer/src/components/status-bar/resource-usage-open-slices.test.ts b/src/renderer/src/components/status-bar/resource-usage-open-slices.test.ts index 85663bc2090..21f3aee3e40 100644 --- a/src/renderer/src/components/status-bar/resource-usage-open-slices.test.ts +++ b/src/renderer/src/components/status-bar/resource-usage-open-slices.test.ts @@ -2,6 +2,8 @@ import { describe, expect, it } from 'vitest' import { getResourceUsageAllWorktrees, getResourceUsageDeferredSshSessionIdsByTabId, + getResourceUsageFolderWorkspaces, + getResourceUsageProjectGroups, getResourceUsagePtyIdsByTabId, getResourceUsageRepos, getResourceUsageRuntimePaneTitlesByTabId, @@ -42,6 +44,19 @@ const worktree = (): AppState['worktreesByRepo'][string][number] => ({ }) describe('resource usage open slices', () => { + it('subscribes to folder and group catalogs only while open', () => { + const folderWorkspaces: AppState['folderWorkspaces'] = [] + const projectGroups: AppState['projectGroups'] = [] + expect(getResourceUsageFolderWorkspaces({ folderWorkspaces }, true)).toBe(folderWorkspaces) + expect(getResourceUsageProjectGroups({ projectGroups }, true)).toBe(projectGroups) + expect(getResourceUsageFolderWorkspaces({ folderWorkspaces }, false)).toBe( + getResourceUsageFolderWorkspaces({ folderWorkspaces: [] }, false) + ) + expect(getResourceUsageProjectGroups({ projectGroups }, false)).toBe( + getResourceUsageProjectGroups({ projectGroups: [] }, false) + ) + }) + it('returns stable empty slices while the popover is closed', () => { const tabsByWorktree = { 'wt-1': [terminalTab('tab-1')] } const ptyIdsByTabId = { 'tab-1': ['pty-1'] } diff --git a/src/renderer/src/components/status-bar/resource-usage-open-slices.ts b/src/renderer/src/components/status-bar/resource-usage-open-slices.ts index ca50f59b8b8..32bd7237397 100644 --- a/src/renderer/src/components/status-bar/resource-usage-open-slices.ts +++ b/src/renderer/src/components/status-bar/resource-usage-open-slices.ts @@ -8,6 +8,8 @@ const EMPTY_DEFERRED_SSH_SESSION_IDS_BY_TAB_ID: AppState['deferredSshSessionIdsB const EMPTY_RUNTIME_PANE_TITLES_BY_TAB_ID: AppState['runtimePaneTitlesByTabId'] = {} const EMPTY_BROWSER_TABS_BY_WORKTREE: AppState['browserTabsByWorktree'] = {} const EMPTY_REPOS: AppState['repos'] = [] +const EMPTY_FOLDER_WORKSPACES: AppState['folderWorkspaces'] = [] +const EMPTY_PROJECT_GROUPS: AppState['projectGroups'] = [] const EMPTY_WORKTREES: ReturnType = [] export function getResourceUsageTabsByWorktree( @@ -65,3 +67,17 @@ export function getResourceUsageAllWorktrees( ): ReturnType { return open ? getAllWorktreesFromState(state) : EMPTY_WORKTREES } + +export function getResourceUsageFolderWorkspaces( + state: Pick, + open: boolean +): AppState['folderWorkspaces'] { + return open ? state.folderWorkspaces : EMPTY_FOLDER_WORKSPACES +} + +export function getResourceUsageProjectGroups( + state: Pick, + open: boolean +): AppState['projectGroups'] { + return open ? state.projectGroups : EMPTY_PROJECT_GROUPS +} diff --git a/src/renderer/src/components/status-bar/resource-workspace-host.ts b/src/renderer/src/components/status-bar/resource-workspace-host.ts new file mode 100644 index 00000000000..5ea2e9df33e --- /dev/null +++ b/src/renderer/src/components/status-bar/resource-workspace-host.ts @@ -0,0 +1,32 @@ +import { parseExecutionHostId } from '../../../../shared/execution-host' +import { parseWorkspaceKey } from '../../../../shared/workspace-scope' +import type { Worktree } from '../../../../shared/worktree/types' +import type { MergeContext } from './resource-usage-merge-types' + +/** Folder catalog row for host/name attribution; a duplicated id cannot choose a host, so it yields nothing. */ +export function resolveResourceFolderWorkspace( + ctx: MergeContext, + worktreeId: string +): Worktree | undefined { + if ( + parseWorkspaceKey(worktreeId)?.type !== 'folder' || + ctx.ambiguousWorktreeIds?.has(worktreeId) + ) { + return undefined + } + return ctx.worktreeById?.get(worktreeId) +} + +export function resolveResourceWorkspaceHost( + ctx: MergeContext, + worktreeId: string, + repoId: string +): { isRemote: boolean; isRuntimeScoped: boolean } { + const folder = resolveResourceFolderWorkspace(ctx, worktreeId) + const host = folder ? parseExecutionHostId(folder.hostId ?? 'local') : null + return { + // Folder siblings may execute on different hosts within the same project group. + isRemote: host ? host.kind === 'ssh' : ctx.repoConnectionIdById.get(repoId) != null, + isRuntimeScoped: host ? host.kind === 'runtime' : ctx.repoRuntimeScopedById.get(repoId) === true + } +} diff --git a/src/renderer/src/components/status-bar/use-resource-usage-actions.test.tsx b/src/renderer/src/components/status-bar/use-resource-usage-actions.test.tsx new file mode 100644 index 00000000000..568afdea1ee --- /dev/null +++ b/src/renderer/src/components/status-bar/use-resource-usage-actions.test.tsx @@ -0,0 +1,77 @@ +// @vitest-environment happy-dom +import { cleanup, renderHook } from '@testing-library/react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { ResourceManagerWorktreeTarget } from './resource-manager-worktree-target' + +const mocks = vi.hoisted(() => ({ + activateAndRevealWorkspace: vi.fn(), + activateAndRevealWorktree: vi.fn(), + worktrees: [] as ResourceManagerWorktreeTarget[] +})) + +vi.mock('@/lib/worktree-activation', () => ({ + activateAndRevealWorkspace: mocks.activateAndRevealWorkspace, + activateAndRevealWorktree: mocks.activateAndRevealWorktree +})) +vi.mock('@/lib/activate-tab-and-focus-pane', () => ({ activateTabAndFocusPane: vi.fn() })) +vi.mock('../../store', () => ({ useAppStore: { getState: () => ({}) } })) +vi.mock('../../store/selectors', () => ({ getAllWorktreesFromState: () => mocks.worktrees })) +vi.mock('../sidebar/delete-worktree-flow', () => ({ runWorktreeDelete: vi.fn() })) + +import { useResourceUsageActions } from './use-resource-usage-actions' + +function renderActions() { + return renderHook(() => + useResourceUsageActions({ + setCollapsedRepos: vi.fn(), + setCollapsedWorktrees: vi.fn(), + tabsByWorktree: {}, + setOpen: vi.fn(), + setActiveView: vi.fn(), + openModal: vi.fn(), + openSpacePage: vi.fn(), + refreshSessions: vi.fn(async () => {}), + removeSession: vi.fn(), + removeSessions: vi.fn(), + sessions: [], + resourceSessionBindings: { + tabsByWorktree: {}, + ptyIdsByTabId: {}, + workspaceSessionReady: true + }, + workspaceSessionReady: true, + killConfirm: null, + setKillConfirm: vi.fn(), + setKilling: vi.fn(), + mountedRef: { current: true }, + cancelPopoverBodyFocusFrame: vi.fn(), + popoverBodyRef: { current: null }, + popoverBodyFocusFrameRef: { current: null } + }) + ).result.current +} + +beforeEach(() => { + mocks.activateAndRevealWorkspace.mockReset() + mocks.activateAndRevealWorktree.mockReset() + mocks.worktrees = [{ id: 'repo::/notes', hostId: 'ssh:box' }] +}) +afterEach(cleanup) + +describe('Resource Manager row navigation', () => { + it('activates a folder workspace row through the workspace dispatcher', () => { + renderActions().navigateToWorktree('folder:notes') + + expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith('folder:notes') + expect(mocks.activateAndRevealWorktree).not.toHaveBeenCalled() + }) + + it('still routes worktree rows through the host-resolved activator', () => { + renderActions().navigateToWorktree('repo::/notes') + + expect(mocks.activateAndRevealWorktree).toHaveBeenCalledWith('repo::/notes', { + executionHostId: 'ssh:box' + }) + expect(mocks.activateAndRevealWorkspace).not.toHaveBeenCalled() + }) +}) diff --git a/src/renderer/src/components/status-bar/use-resource-usage-actions.ts b/src/renderer/src/components/status-bar/use-resource-usage-actions.ts index 06a2d0ad177..f94237a0bbe 100644 --- a/src/renderer/src/components/status-bar/use-resource-usage-actions.ts +++ b/src/renderer/src/components/status-bar/use-resource-usage-actions.ts @@ -1,11 +1,12 @@ import { useCallback, type Dispatch, type MutableRefObject, type SetStateAction } from 'react' -import { activateAndRevealWorktree } from '@/lib/worktree-activation' +import { activateAndRevealWorkspace, activateAndRevealWorktree } from '@/lib/worktree-activation' import { activateTabAndFocusPane } from '@/lib/activate-tab-and-focus-pane' import { useAppStore } from '../../store' import type { AppState } from '../../store/types' import { getAllWorktreesFromState } from '../../store/selectors' import { runWorktreeDelete } from '../sidebar/delete-worktree-flow' import { ORPHAN_WORKTREE_ID } from '../../../../shared/constants' +import { parseWorkspaceKey } from '../../../../shared/workspace-scope' import { UNATTRIBUTED_REPO_ID } from './mergeSnapshotAndSessions' import type { DaemonSession, UnifiedSessionRow } from './resource-usage-merge-types' import type { ResourceSessionBindingInputs } from './resource-session-bindings' @@ -92,6 +93,11 @@ export function useResourceUsageActions({ if (worktreeId === ORPHAN_WORKTREE_ID || worktreeId.startsWith(`${UNATTRIBUTED_REPO_ID}::`)) { return } + // Why: the target resolve below only knows worktrees, so a folder key never matched; the folder activator owns host and path-status gating. + if (parseWorkspaceKey(worktreeId)?.type === 'folder') { + activateAndRevealWorkspace(worktreeId) + return + } const target = resolveResourceManagerWorktreeTarget( worktreeId, getAllWorktreesFromState(useAppStore.getState()) diff --git a/src/renderer/src/components/status-bar/use-resource-usage-derived-model.test.tsx b/src/renderer/src/components/status-bar/use-resource-usage-derived-model.test.tsx new file mode 100644 index 00000000000..f5e1bac9dbb --- /dev/null +++ b/src/renderer/src/components/status-bar/use-resource-usage-derived-model.test.tsx @@ -0,0 +1,196 @@ +// @vitest-environment happy-dom +import { cleanup, renderHook } from '@testing-library/react' +import { afterEach, describe, expect, it } from 'vitest' +import type { BrowserWorkspace } from '../../../../shared/browser-workspace-types' +import type { MemorySnapshot, WorktreeMemory } from '../../../../shared/process-stats-types' +import type { Worktree } from '../../../../shared/worktree/types' +import type { ProjectGroup } from '../../../../shared/project-group-types' +import type { DaemonSession } from './resource-usage-merge-types' +import { useResourceUsageDerivedModel } from './use-resource-usage-derived-model' + +const local = { + id: 'folder:local', + repoId: 'folder-workspace:group', + displayName: 'Local notes', + hostId: 'local' +} as Worktree +const sampled: WorktreeMemory = { + worktreeId: local.id, + worktreeName: 'Published local notes', + repoId: local.repoId, + repoName: 'Local project', + cpu: 2, + memory: 2048, + history: [1024, 2048], + sessions: [{ sessionId: 'sampled', paneKey: null, pid: 123, cpu: 2, memory: 2048 }] +} +const group = { id: 'group', name: 'Local project', executionHostId: 'local' } as ProjectGroup + +function derive( + worktrees: Worktree[], + sessions: DaemonSession[] = [], + row = sampled, + projectGroups = [group], + browserTabsByWorktree: Record = {} +) { + const snapshot = { + worktrees: [row], + host: { totalMemory: 16384 }, + totalMemory: 2048, + totalCpu: 2, + processMemoryMetric: 'rss' + } as MemorySnapshot + return renderHook(() => + useResourceUsageDerivedModel({ + open: true, + resourceSnapshot: snapshot, + sessions, + resourceSessionBindings: { + tabsByWorktree: {}, + ptyIdsByTabId: {}, + workspaceSessionReady: true + }, + runtimePaneTitlesByTabId: {}, + repos: [], + allWorktrees: worktrees, + projectGroups, + browserTabsByWorktree, + workspaceSessionReady: true, + sessionCount: sessions.length, + sessionsError: false, + memorySnapshotError: null, + snapshot, + spaceScanReady: false + }) + ).result.current.unifiedRepos +} + +afterEach(cleanup) + +describe('Resource Manager folder ownership', () => { + it.each(['ssh:box', 'runtime:paired'] as const)( + 'keeps local samples when a sibling folder belongs to %s', + (hostId) => { + const sibling = { ...local, id: 'folder:remote', displayName: 'Remote notes', hostId } + const groups = derive( + [local, sibling], + [ + { + id: 'remote-session', + worktreeId: sibling.id, + cwd: '/notes', + title: 'Shell', + agentOwnership: 'present' + } + ] + ) + + expect(groups).toHaveLength(1) + expect(groups[0]).toMatchObject({ + cpu: 2, + memory: 2048, + hasRemoteChildren: hostId === 'ssh:box' + }) + expect(groups[0].worktrees[0]).toMatchObject({ + worktreeId: local.id, + worktreeName: local.displayName, + isRemote: false, + cpu: 2, + memory: 2048, + sessions: [{ sessionId: 'sampled', memory: 2048 }] + }) + if (hostId === 'ssh:box') { + expect(groups[0].worktrees[1]).toMatchObject({ + worktreeId: sibling.id, + isRemote: true, + cpu: null, + memory: null + }) + } else { + expect(groups[0].worktrees).toHaveLength(1) + } + } + ) + + it.each([false, true])( + 'preserves sampled ownership with duplicate folder ids (reverse=%s)', + (reverse) => { + const remote = { + ...local, + repoId: 'folder-workspace:remote', + displayName: 'Foreign notes', + hostId: 'runtime:paired' + } as Worktree + const groups = derive(reverse ? [remote, local] : [local, remote]) + + expect(groups).toHaveLength(1) + expect(groups[0]).toMatchObject({ + repoId: sampled.repoId, + repoName: sampled.repoName, + memory: 2048 + }) + expect(groups[0].worktrees[0]).toMatchObject({ + worktreeName: sampled.worktreeName, + isRemote: false, + memory: 2048, + history: sampled.history + }) + } + ) + + it.each(['repo::/notes', 'folder:shared'])( + 'keeps browser-only rows when %s exists on two hosts', + (worktreeId) => { + const here = { ...local, id: worktreeId, repoId: 'repo', displayName: 'Notes' } as Worktree + const there = { ...here, hostId: 'ssh:box' } as Worktree + const browser = { id: 'browser-1', worktreeId, title: 'Docs' } as BrowserWorkspace + const groups = derive([here, there], [], sampled, [group], { [worktreeId]: [browser] }) + + expect(groups.find((project) => project.repoId === 'repo')).toMatchObject({ + worktrees: [{ worktreeId, isRemote: false, browsers: [browser] }] + }) + } + ) + + it('does not replace a sampled project name with a same-id group from another host', () => { + const foreignGroup = { ...group, name: 'Foreign project', executionHostId: 'runtime:paired' } + const groups = derive([local], [], sampled, [group, foreignGroup]) + expect(groups[0].repoName).toBe('Local project') + }) + + it('keeps git snapshot identity when the catalog contains a different host', () => { + const row = { ...sampled, worktreeId: 'repo::/notes', repoId: 'repo' } + const foreign = { + ...local, + id: row.worktreeId, + repoId: 'foreign-repo', + displayName: 'Foreign notes', + hostId: 'runtime:paired' + } as Worktree + const groups = derive([foreign], [], row) + + expect(groups[0]).toMatchObject({ repoId: row.repoId, repoName: row.repoName, memory: 2048 }) + expect(groups[0].worktrees[0].worktreeName).toBe(row.worktreeName) + }) + + it('keeps git daemon identity when the catalog contains a different host', () => { + const worktreeId = 'repo::/notes' + const foreign = { + ...local, + id: worktreeId, + repoId: 'foreign-repo', + displayName: 'Foreign notes', + hostId: 'runtime:paired' + } as Worktree + const groups = derive( + [foreign], + [{ id: 'git-session', worktreeId, cwd: '/notes', title: '', agentOwnership: 'absent' }] + ) + + expect(groups.find((project) => project.repoId === 'repo')).toMatchObject({ + repoName: 'repo', + worktrees: [{ worktreeId, worktreeName: 'notes', isRemote: false }] + }) + expect(groups.some((project) => project.repoId === foreign.repoId)).toBe(false) + }) +}) diff --git a/src/renderer/src/components/status-bar/use-resource-usage-derived-model.ts b/src/renderer/src/components/status-bar/use-resource-usage-derived-model.ts index 6cf5c780351..cc56fd7f357 100644 --- a/src/renderer/src/components/status-bar/use-resource-usage-derived-model.ts +++ b/src/renderer/src/components/status-bar/use-resource-usage-derived-model.ts @@ -17,6 +17,7 @@ import { getResourceMemoryMetricCopy } from './resource-memory-metric-copy' import { formatMemory } from './resource-usage-metrics' +import { findAmbiguousWorktreeIds, findDuplicateIds } from '../../lib/unified-tab-host-ownership' export function useResourceUsageDerivedModel({ open, @@ -26,6 +27,7 @@ export function useResourceUsageDerivedModel({ runtimePaneTitlesByTabId, repos, allWorktrees, + projectGroups, browserTabsByWorktree, workspaceSessionReady, sessionCount, @@ -41,6 +43,7 @@ export function useResourceUsageDerivedModel({ runtimePaneTitlesByTabId: AppState['runtimePaneTitlesByTabId'] repos: AppState['repos'] allWorktrees: Worktree[] + projectGroups: AppState['projectGroups'] browserTabsByWorktree: AppState['browserTabsByWorktree'] workspaceSessionReady: boolean sessionCount: number @@ -57,8 +60,14 @@ export function useResourceUsageDerivedModel({ map.set(repo.id, display) } } + const ambiguousGroupIds = findDuplicateIds(projectGroups) + for (const group of projectGroups) { + if (!ambiguousGroupIds.has(group.id)) { + map.set(`folder-workspace:${group.id}`, group.name) + } + } return map - }, [repos]) + }, [repos, projectGroups]) // Why: non-null connectionId is the only honest "remote" signal (SSH PTYs run remote); build from the store, not a missing memory sample. const repoConnectionIdById = useMemo(() => { @@ -83,6 +92,9 @@ export function useResourceUsageDerivedModel({ () => new Map(allWorktrees.map((worktree) => [worktree.id, worktree])), [allWorktrees] ) + // Why: a bare resource identity cannot choose between the same workspace id on different + // hosts, but the id still exists; keep the map whole and let the merge gate attribution only. + const ambiguousWorktreeIds = useMemo(() => findAmbiguousWorktreeIds(allWorktrees), [allWorktrees]) // Why: skip the merge when closed; the always-mounted segment recomputing on every keystroke-driven store mutation made the app laggy. const unifiedRepos = useMemo( @@ -98,7 +110,8 @@ export function useResourceUsageDerivedModel({ repoConnectionIdById, repoRuntimeScopedById, browserTabsByWorktree, - worktreeById + worktreeById, + ambiguousWorktreeIds }) : [], [ @@ -111,7 +124,8 @@ export function useResourceUsageDerivedModel({ repoConnectionIdById, repoRuntimeScopedById, browserTabsByWorktree, - worktreeById + worktreeById, + ambiguousWorktreeIds ] ) diff --git a/src/renderer/src/components/status-bar/use-resource-usage-status-controller.ts b/src/renderer/src/components/status-bar/use-resource-usage-status-controller.ts index 9dd9c22666d..e565d0cc64b 100644 --- a/src/renderer/src/components/status-bar/use-resource-usage-status-controller.ts +++ b/src/renderer/src/components/status-bar/use-resource-usage-status-controller.ts @@ -5,10 +5,13 @@ import { useDaemonActions } from '../shared/useDaemonActions' import type { UnifiedSessionRow } from './resource-usage-merge-types' import type { ResourceSessionBindingInputs } from './resource-session-bindings' import type { SortOption } from './resource-usage-resource-tree' +import { folderWorkspaceToWorktree } from '../../../../shared/folder-workspace-worktree' import { getResourceUsageAllWorktrees, getResourceUsageBrowserTabsByWorktree, getResourceUsageDeferredSshSessionIdsByTabId, + getResourceUsageFolderWorkspaces, + getResourceUsageProjectGroups, getResourceUsagePtyIdsByTabId, getResourceUsageRepos, getResourceUsageRuntimePaneTitlesByTabId, @@ -67,7 +70,13 @@ export function useResourceUsageStatusController() { getResourceUsageRuntimePaneTitlesByTabId(s, open) ) const repos = useAppStore((s) => getResourceUsageRepos(s, open)) - const allWorktrees = useAppStore((s) => getResourceUsageAllWorktrees(s, open)) + const gitWorktrees = useAppStore((s) => getResourceUsageAllWorktrees(s, open)) + const folders = useAppStore((s) => getResourceUsageFolderWorkspaces(s, open)) + const projectGroups = useAppStore((s) => getResourceUsageProjectGroups(s, open)) + const allWorktrees = useMemo( + () => [...gitWorktrees, ...folders.map(folderWorkspaceToWorktree)], + [gitWorktrees, folders] + ) const tabsByWorktree = useAppStore((s) => getResourceUsageTabsByWorktree(s, open)) const browserTabsByWorktree = useAppStore((s) => getResourceUsageBrowserTabsByWorktree(s, open)) // Why: full binding maps stay behind open sentinels so unchanged counts don't rerender the closed segment. @@ -186,6 +195,7 @@ export function useResourceUsageStatusController() { runtimePaneTitlesByTabId, repos, allWorktrees, + projectGroups, browserTabsByWorktree, workspaceSessionReady, sessionCount: sessionInventory.count, diff --git a/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts b/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts index 1b34ff4712f..970968a132c 100644 --- a/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts +++ b/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts @@ -202,6 +202,11 @@ function findChildrenByType(node: unknown, typeName: string): ReactElementLike[] if (matchedName === typeName) { results.push(el) } + if (matchedName === 'TabBarStaticCreateMenu' && typeof el.type === 'function') { + // Expand the deferred pure menu component in this shallow renderer. + visit(el.type(el.props)) + return + } if (el.props && 'children' in el.props) { visit(el.props.children) } diff --git a/src/renderer/src/components/tab-bar/tab-bar-static-create-menu.tsx b/src/renderer/src/components/tab-bar/tab-bar-static-create-menu.tsx index 9a7a19a9186..45c39c2da55 100644 --- a/src/renderer/src/components/tab-bar/tab-bar-static-create-menu.tsx +++ b/src/renderer/src/components/tab-bar/tab-bar-static-create-menu.tsx @@ -13,7 +13,7 @@ import { import type { TabBarProps } from './tab-bar-props' import { resolveWindowsShellLaunchTarget } from './windows-shell-launch' -export function renderTabBarStaticCreateMenu({ +export function TabBarStaticCreateMenu({ terminalOnly, mobileEmulatorEnabled, managedBrowserCreationEnabled, diff --git a/src/renderer/src/components/tab-bar/tab-bar-surface.tsx b/src/renderer/src/components/tab-bar/tab-bar-surface.tsx index dcf22611227..0e9540870e3 100644 --- a/src/renderer/src/components/tab-bar/tab-bar-surface.tsx +++ b/src/renderer/src/components/tab-bar/tab-bar-surface.tsx @@ -22,7 +22,7 @@ import type { TabBarCreateMenuController } from './use-tab-bar-create-menu-contr import type { TabBarItemProjection } from './use-tab-bar-item-projection' import type { TabBarItem } from './tab-bar-item-model' import { renderTabBarItems } from './tab-bar-item-surface' -import { renderTabBarStaticCreateMenu } from './tab-bar-static-create-menu' +import { TabBarStaticCreateMenu } from './tab-bar-static-create-menu' import ClientHostedBrowserTabRows from './ClientHostedBrowserTabRows' import type { ClientHostedBrowserRow } from '../../../../shared/client-hosted-browser-rows' @@ -99,24 +99,6 @@ export function renderTabBarSurface({ activeClientHostedBrowserRowId, togglePinned }) - const standardCreateMenuItems = renderTabBarStaticCreateMenu({ - props, - terminalOnly, - mobileEmulatorEnabled, - managedBrowserCreationEnabled, - mobileEmulatorCreationEnabled, - workspaceHasSimulatorTab, - showMobileEmulatorIntroCallout, - windowsShellEntries, - defaultWindowsPowerShellImplementation, - pwshAvailable: windowsTerminalCapabilities.pwshAvailable, - newTerminalShortcut, - newBrowserShortcut, - newSimulatorShortcut, - newFileShortcut, - openMarkdownShortcut, - queueNewActiveTerminalFocusAfterNewTabMenuClose - }) return (
: null} ) : null} - {showStaticCreateMenuItems ? standardCreateMenuItems : null} + {showStaticCreateMenuItems ? ( + + ) : null} {showStaticCreateMenuItems && showAgentLaunchItems ? ( <> diff --git a/src/renderer/src/components/tab-bar/tab-create-entry-url-classification.test.ts b/src/renderer/src/components/tab-bar/tab-create-entry-url-classification.test.ts new file mode 100644 index 00000000000..3497cef7cac --- /dev/null +++ b/src/renderer/src/components/tab-bar/tab-create-entry-url-classification.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'vitest' +import { classifyHostUrl } from './tab-create-entry-url-classification' + +// Why this file exists: the suffix check behind `host/path` navigation moved off `psl.isValid`, and +// nothing else in the tree exercises it directly. These cases pin the listed/unlisted split that +// decides whether a typed string navigates or falls through to search. +describe('classifyHostUrl suffix gate', () => { + it('navigates for a listed suffix carrying a path', () => { + expect(classifyHostUrl('example.com/foo')).toEqual({ + kind: 'host-url', + url: 'https://example.com/foo' + }) + expect(classifyHostUrl('example.co.uk/foo')).toEqual({ + kind: 'host-url', + url: 'https://example.co.uk/foo' + }) + }) + + // The PRIVATE section has to stay in: these are navigable hosts, not search terms. + it('navigates for a private-section suffix carrying a path', () => { + expect(classifyHostUrl('foo.github.io/bar')).toEqual({ + kind: 'host-url', + url: 'https://foo.github.io/bar' + }) + expect(classifyHostUrl('foo.vercel.app/bar')).toEqual({ + kind: 'host-url', + url: 'https://foo.vercel.app/bar' + }) + }) + + // Why this is the fix: psl's 2024 snapshot did not know `api.br`, so `isValid` called it a domain + // and a typed `api.br/x` navigated to a bare public suffix instead of searching. + it('refuses a bare public suffix carrying a path', () => { + expect(classifyHostUrl('api.br/foo')).toBeNull() + expect(classifyHostUrl('co.uk/foo')).toBeNull() + expect(classifyHostUrl('github.io/foo')).toBeNull() + }) + + it('keeps localhost and IPv4 on http without consulting the suffix list', () => { + expect(classifyHostUrl('localhost:3000/foo')).toEqual({ + kind: 'host-url', + url: 'http://localhost:3000/foo' + }) + expect(classifyHostUrl('127.0.0.1:8080/foo')).toEqual({ + kind: 'host-url', + url: 'http://127.0.0.1:8080/foo' + }) + }) + + // The gate only applies once an authority ends; a bare host still navigates so unlisted intranet + // names typed on their own are not forced into search. + it('leaves a bare host unfiltered by the suffix list', () => { + expect(classifyHostUrl('api.br')).toEqual({ kind: 'host-url', url: 'https://api.br/' }) + }) +}) diff --git a/src/renderer/src/components/tab-bar/tab-create-entry-url-classification.ts b/src/renderer/src/components/tab-bar/tab-create-entry-url-classification.ts index a18f59d1ccf..ef99f648b9c 100644 --- a/src/renderer/src/components/tab-bar/tab-create-entry-url-classification.ts +++ b/src/renderer/src/components/tab-bar/tab-create-entry-url-classification.ts @@ -1,5 +1,5 @@ import { translate } from '@/i18n/i18n' -import { isValid as isListedDomain } from 'psl' +import { parse as parseDomain } from 'tldts' import { classifySchemeLessLocalDevAddress } from '../../../../shared/browser-url' const HOST_FILE_EXTENSIONS = new Set([ @@ -54,6 +54,13 @@ function parseHttpUrl(query: string): ExplicitUrlClassification { } } +// Why: a bare public suffix (`api.br`) is not a navigable host, so the typed string stays a search. +// tldts' PRIVATE section is included so `foo.github.io` still reads as a domain. +function isListedDomain(host: string): boolean { + const parsed = parseDomain(host, { allowPrivateDomains: true }) + return parsed.domain !== null && (parsed.isIcann === true || parsed.isPrivate === true) +} + function splitHostCandidate(query: string): { host: string; port: string | null } | null { if (/[\\\s]/.test(query)) { return null diff --git a/src/renderer/src/components/tab-group/tab-drag-pointer-sensor.test.ts b/src/renderer/src/components/tab-group/tab-drag-pointer-sensor.test.ts new file mode 100644 index 00000000000..55f24f0c3e5 --- /dev/null +++ b/src/renderer/src/components/tab-group/tab-drag-pointer-sensor.test.ts @@ -0,0 +1,114 @@ +// @vitest-environment happy-dom +import type { PointerSensorOptions, SensorProps } from '@dnd-kit/core' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { TabDragPointerSensor } from './tab-drag-pointer-sensor' + +function startSensor(options: PointerSensorOptions = {}) { + const callbacks = { + onAbort: vi.fn(), + onPending: vi.fn(), + onStart: vi.fn(), + onCancel: vi.fn(), + onMove: vi.fn(), + onEnd: vi.fn() + } + new TabDragPointerSensor({ + active: 'tab-1', + event: new PointerEvent('pointerdown', { clientX: 10, clientY: 10 }), + options, + ...callbacks + } as unknown as SensorProps) + return callbacks +} + +function movePointer(): void { + document.dispatchEvent(new PointerEvent('pointermove', { clientX: 100, clientY: 50 })) +} + +beforeEach(() => vi.useFakeTimers()) + +afterEach(() => { + window.dispatchEvent(new Event('resize')) + vi.runOnlyPendingTimers() + vi.restoreAllMocks() + vi.useRealTimers() +}) + +describe('tab drag pointer sensor cancellation', () => { + it('cancels an active gesture on blur and stops subsequent moves and drops', () => { + const callbacks = startSensor() + expect(callbacks.onStart).toHaveBeenCalledOnce() + movePointer() + expect(callbacks.onMove).toHaveBeenCalledOnce() + + window.dispatchEvent(new Event('blur')) + expect(callbacks.onCancel).toHaveBeenCalledOnce() + expect(callbacks.onAbort).not.toHaveBeenCalled() + movePointer() + document.dispatchEvent(new PointerEvent('pointerup')) + + expect(callbacks.onMove).toHaveBeenCalledOnce() + expect(callbacks.onEnd).not.toHaveBeenCalled() + }) + + it.each([ + { distance: 12 }, + { delay: 100, tolerance: 10 } + ])('aborts a pending gesture on blur before activation: %j', (activationConstraint) => { + const callbacks = startSensor({ activationConstraint }) + expect(callbacks.onStart).not.toHaveBeenCalled() + + window.dispatchEvent(new Event('blur')) + expect(callbacks.onAbort).toHaveBeenCalledWith('tab-1') + expect(callbacks.onCancel).toHaveBeenCalledOnce() + vi.advanceTimersByTime(150) + movePointer() + movePointer() + document.dispatchEvent(new PointerEvent('pointerup')) + + expect(callbacks.onStart).not.toHaveBeenCalled() + expect(callbacks.onMove).not.toHaveBeenCalled() + expect(callbacks.onEnd).not.toHaveBeenCalled() + }) + + it('still completes an uninterrupted gesture on pointerup', () => { + const callbacks = startSensor() + movePointer() + document.dispatchEvent(new PointerEvent('pointerup')) + window.dispatchEvent(new Event('blur')) + movePointer() + + expect(callbacks.onEnd).toHaveBeenCalledOnce() + expect(callbacks.onMove).toHaveBeenCalledOnce() + expect(callbacks.onCancel).not.toHaveBeenCalled() + expect(callbacks.onAbort).not.toHaveBeenCalled() + }) + + it('does not restart when a captured activation callback arrives after cancellation', () => { + const schedule = vi.spyOn(window, 'setTimeout') + const callbacks = startSensor({ activationConstraint: { delay: 100, tolerance: 10 } }) + const activate = schedule.mock.calls[0]?.[0] + expect(activate).toBeTypeOf('function') + window.dispatchEvent(new Event('blur')) + ;(activate as () => void)() + + expect(callbacks.onStart).not.toHaveBeenCalled() + expect(callbacks.onCancel).toHaveBeenCalledOnce() + }) + + it('ignores the old Escape listener while a new gesture is active', () => { + const previous = startSensor() + window.dispatchEvent(new Event('blur')) + const current = startSensor() + movePointer() + document.dispatchEvent(new KeyboardEvent('keydown', { code: 'Escape' })) + document.dispatchEvent(new PointerEvent('pointerup')) + + expect(previous.onCancel).toHaveBeenCalledOnce() + expect(previous.onMove).not.toHaveBeenCalled() + expect(current.onStart).toHaveBeenCalledOnce() + expect(current.onMove).toHaveBeenCalledOnce() + expect(current.onCancel).toHaveBeenCalledOnce() + expect(current.onEnd).not.toHaveBeenCalled() + }) +}) diff --git a/src/renderer/src/components/tab-group/tab-drag-pointer-sensor.ts b/src/renderer/src/components/tab-group/tab-drag-pointer-sensor.ts index ab96dc5e394..40448c6b197 100644 --- a/src/renderer/src/components/tab-group/tab-drag-pointer-sensor.ts +++ b/src/renderer/src/components/tab-group/tab-drag-pointer-sensor.ts @@ -140,6 +140,7 @@ export class TabDragPointerSensor implements SensorInstance { autoScrollEnabled = true private activated = false + private ended = false private readonly document: Document private readonly initialCoordinates: PointerCoordinates private readonly pointerDownTime = performance.now() @@ -174,6 +175,7 @@ export class TabDragPointerSensor implements SensorInstance { this.windowListeners.add(win, 'dragstart', preventDefault) this.windowListeners.add(win, 'visibilitychange', this.handleCancel) this.windowListeners.add(win, 'contextmenu', preventDefault) + this.windowListeners.add(win, 'blur', this.handleCancel) this.windowListeners.add(win, 'focus', this.handleCancel) this.documentListeners.add(this.document, 'keydown', this.handleKeydown) @@ -200,6 +202,7 @@ export class TabDragPointerSensor implements SensorInstance { } private detach(): void { + this.ended = true this.pointerListeners.removeAll() this.windowListeners.removeAll() window.setTimeout(this.documentListeners.removeAll, 50) @@ -217,7 +220,7 @@ export class TabDragPointerSensor implements SensorInstance { } private handleStart(): void { - if (this.activated) { + if (this.activated || this.ended) { return } this.activated = true @@ -228,6 +231,9 @@ export class TabDragPointerSensor implements SensorInstance { } private handleMove(event: PointerEvent): void { + if (this.ended) { + return + } const coordinates = getPointerCoordinates(event) const { activationConstraint } = this.props.options if (!coordinates) { @@ -277,6 +283,9 @@ export class TabDragPointerSensor implements SensorInstance { } private handleEnd(): void { + if (this.ended) { + return + } this.detach() if (!this.activated) { this.props.onAbort(this.props.active) @@ -285,6 +294,9 @@ export class TabDragPointerSensor implements SensorInstance { } private handleCancel(): void { + if (this.ended) { + return + } this.detach() if (!this.activated) { this.props.onAbort(this.props.active) diff --git a/src/renderer/src/components/tab-group/useTabDragSplit.test.ts b/src/renderer/src/components/tab-group/useTabDragSplit.test.ts index bdd5edd97af..f57380b09b1 100644 --- a/src/renderer/src/components/tab-group/useTabDragSplit.test.ts +++ b/src/renderer/src/components/tab-group/useTabDragSplit.test.ts @@ -16,6 +16,8 @@ import { useTabDragSplit } from './useTabDragSplit' +globalThis.IS_REACT_ACT_ENVIRONMENT = true + vi.mock('../browser-pane/host-guest/webview-registry', () => ({ acquireWebviewsDragPassthrough: vi.fn(() => vi.fn()) })) @@ -94,10 +96,13 @@ function makeDragEvent(activeData: TabDragItemData, pointer: { x: number; y: num } } -function renderDragHook(): ReturnType { +function renderDragHook( + onRender?: (drag: ReturnType) => void +): ReturnType { let result: ReturnType | null = null function Probe(): null { result = useTabDragSplit({ worktreeId: WT }) + onRender?.(result) return null } @@ -267,6 +272,68 @@ describe('canDropTabIntoPaneBody', () => { }) describe('useTabDragSplit', () => { + it.each(['split', 'insertion'])( + 'does not restore a %s preview after blur cleared the drag', + async (preview) => { + if (preview === 'split') { + addPanelGeometry( + 'group-2', + rect({ left: 500, top: 0, width: 400, height: 600 }), + rect({ left: 500, top: 32, width: 400, height: 568 }) + ) + } + const onRender = vi.fn() + const drag = renderDragHook(onRender) + const event = { + ...makeDragEvent(makeDragData('group-1'), { x: 880, y: 300 }), + ...(preview === 'insertion' + ? { + over: { + data: { current: makeDragData('group-2', 'tab-2') }, + rect: rect({ left: 500, top: 0, width: 400, height: 32 }) + } + } + : {}) + } + const previewKey = preview === 'split' ? 'hoveredDropTarget' : 'hoveredTabInsertion' + act(() => drag.onDragStart(event as unknown as Parameters[0])) + act(() => drag.onDragMove(event as unknown as Parameters[0])) + expect(onRender.mock.lastCall?.[0][previewKey]).not.toBeNull() + + await act(async () => { + window.dispatchEvent(new Event('blur')) + await new Promise((resolve) => window.setTimeout(resolve, 0)) + }) + expect(onRender.mock.lastCall?.[0][previewKey]).toBeNull() + act(() => drag.onDragMove(event as unknown as Parameters[0])) + + expect(onRender.mock.lastCall?.[0][previewKey]).toBeNull() + expect(drag.isTabDragActiveRef.current).toBe(false) + } + ) + + it('does not commit a drop after blur cleared the drag', async () => { + addPanelGeometry( + 'group-2', + rect({ left: 500, top: 0, width: 400, height: 600 }), + rect({ left: 500, top: 32, width: 400, height: 568 }) + ) + const dropUnifiedTab = vi.fn(() => true) + const reorderUnifiedTabs = vi.fn() + useAppStore.setState({ dropUnifiedTab, reorderUnifiedTabs }) + const drag = renderDragHook() + const event = makeDragEvent(makeDragData('group-1'), { x: 880, y: 300 }) + act(() => drag.onDragStart(event as unknown as Parameters[0])) + await act(async () => { + window.dispatchEvent(new Event('blur')) + await new Promise((resolve) => window.setTimeout(resolve, 0)) + }) + act(() => drag.onDragEnd(event as unknown as Parameters[0])) + + expect(dropUnifiedTab).not.toHaveBeenCalled() + expect(reorderUnifiedTabs).not.toHaveBeenCalled() + }) + it.each(['pointerup', 'pointercancel', 'blur', 'focus'])( 'clears a stuck active drag when %s arrives without a dnd end event', async (eventName) => { diff --git a/src/renderer/src/components/tab-group/useTabDragSplit.ts b/src/renderer/src/components/tab-group/useTabDragSplit.ts index a3520174aed..ca1724e2bf4 100644 --- a/src/renderer/src/components/tab-group/useTabDragSplit.ts +++ b/src/renderer/src/components/tab-group/useTabDragSplit.ts @@ -209,6 +209,10 @@ export function useTabDragSplit({ const onDragMove = useCallback( (event: DragMoveEvent) => { + // A missed-end cleanup can run before dnd-kit delivers its last move. + if (!tabDragActiveRef.current) { + return + } handleDragUpdate(event) }, [handleDragUpdate] @@ -221,6 +225,10 @@ export function useTabDragSplit({ const onDragEnd = useCallback( (event: DragEndEvent) => { + if (!tabDragActiveRef.current) { + finishDrag(true) + return + } commitTabDragDrop({ event, worktreeId, diff --git a/src/renderer/src/components/terminal-pane/CloseTerminalDialog.test.tsx b/src/renderer/src/components/terminal-pane/CloseTerminalDialog.test.tsx index d2b8efd3efa..0531720fe47 100644 --- a/src/renderer/src/components/terminal-pane/CloseTerminalDialog.test.tsx +++ b/src/renderer/src/components/terminal-pane/CloseTerminalDialog.test.tsx @@ -4,6 +4,11 @@ import { act } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, describe, expect, it, vi } from 'vitest' import CloseTerminalDialog from './CloseTerminalDialog' +import { translate } from '@/i18n/i18n' + +vi.mock('@/i18n/i18n', () => ({ + translate: vi.fn((_key: string, fallback: string) => fallback) +})) const mountedRoots: Root[] = [] @@ -49,6 +54,22 @@ describe('CloseTerminalDialog', () => { document.body.innerHTML = '' }) + it('does no dialog-copy work while closed, then builds the opened confirmation', async () => { + const container = document.createElement('div') + document.body.appendChild(container) + const root = createRoot(container) + mountedRoots.push(root) + const props = { onCancel: vi.fn(), onConfirm: vi.fn() } + vi.mocked(translate).mockClear() + + await act(async () => root.render()) + expect(translate).not.toHaveBeenCalled() + + await act(async () => root.render()) + expect(document.body.textContent).toContain('Stop running command?') + expect(translate).toHaveBeenCalled() + }) + it('renders running command copy and confirms without skipping by default', async () => { const onConfirm = vi.fn() diff --git a/src/renderer/src/components/terminal-pane/CloseTerminalDialog.tsx b/src/renderer/src/components/terminal-pane/CloseTerminalDialog.tsx index c4da5244457..9edbca8b22e 100644 --- a/src/renderer/src/components/terminal-pane/CloseTerminalDialog.tsx +++ b/src/renderer/src/components/terminal-pane/CloseTerminalDialog.tsx @@ -70,71 +70,104 @@ export default function CloseTerminalDialog({ }} > - - - {isAgent - ? translate( - 'auto.components.terminal.pane.CloseTerminalDialog.stop_agent_title', - 'Stop this agent?' - ) - : translate( - 'auto.components.terminal.pane.CloseTerminalDialog.stop_command_title', - 'Stop running command?' - )} - - - {isAgent - ? translate( - 'auto.components.terminal.pane.CloseTerminalDialog.stop_agent_description', - "Closing this terminal will stop the agent's current work." - ) - : translate( - 'auto.components.terminal.pane.CloseTerminalDialog.stop_command_description', - 'Closing this terminal will stop the command running inside it.' - )} - - - {trimmedTabLabel ? ( -

- {trimmedTabLabel} -

- ) : null} -
- setDontAskAgain(checked === true)} - /> - -
- - - - +
) } + +// Keep translation and element construction behind the dialog portal's mount boundary. +function CloseTerminalDialogBody({ + isAgent, + trimmedTabLabel, + checkboxId, + dontAskAgain, + setDontAskAgain, + onCancel, + onConfirm +}: { + isAgent: boolean + trimmedTabLabel: string | undefined + checkboxId: string + dontAskAgain: boolean + setDontAskAgain: (value: boolean) => void + onCancel: () => void + onConfirm: (dontAskAgain: boolean) => void +}): React.JSX.Element { + return ( + <> + + + {isAgent + ? translate( + 'auto.components.terminal.pane.CloseTerminalDialog.stop_agent_title', + 'Stop this agent?' + ) + : translate( + 'auto.components.terminal.pane.CloseTerminalDialog.stop_command_title', + 'Stop running command?' + )} + + + {isAgent + ? translate( + 'auto.components.terminal.pane.CloseTerminalDialog.stop_agent_description', + "Closing this terminal will stop the agent's current work." + ) + : translate( + 'auto.components.terminal.pane.CloseTerminalDialog.stop_command_description', + 'Closing this terminal will stop the command running inside it.' + )} + + + {trimmedTabLabel ? ( +

+ {trimmedTabLabel} +

+ ) : null} +
+ setDontAskAgain(checked === true)} + /> + +
+ + + + + + ) +} diff --git a/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx b/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx index 58a39f28a50..bd25abcce74 100644 --- a/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx +++ b/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx @@ -2,6 +2,7 @@ import React from 'react' import { renderToStaticMarkup } from 'react-dom/server' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import TerminalContextMenu from './TerminalContextMenu' +import { translate } from '@/i18n/i18n' import type { KeybindingOverrides } from '../../../../shared/keybindings' type ItemProps = { onSelect?: () => void; children?: React.ReactNode } @@ -13,9 +14,12 @@ vi.mock('@/components/ui/dropdown-menu', async () => { const React_ = await import('react') const passthrough = ({ children }: { children?: React.ReactNode }) => React_.createElement(React_.Fragment, null, children) + const OpenContext = React_.createContext(false) return { - DropdownMenu: passthrough, - DropdownMenuContent: passthrough, + DropdownMenu: ({ open, children }: { open: boolean; children?: React.ReactNode }) => + React_.createElement(OpenContext.Provider, { value: open }, children), + DropdownMenuContent: ({ children }: { children?: React.ReactNode }) => + React_.useContext(OpenContext) ? passthrough({ children }) : null, DropdownMenuLabel: passthrough, DropdownMenuSeparator: () => null, DropdownMenuShortcut: ({ children }: { children?: React.ReactNode }) => { @@ -36,7 +40,7 @@ vi.mock('@/components/ui/dropdown-menu', async () => { } } }) -vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback })) +vi.mock('@/i18n/i18n', () => ({ translate: vi.fn((_key: string, fallback: string) => fallback) })) vi.mock('@/lib/agent-catalog', () => ({ AgentIcon: () => null })) vi.mock('./terminal-context-menu-dismiss', () => ({ shouldIgnoreTerminalMenuPointerDownOutside: () => false @@ -104,6 +108,7 @@ function renderMenu(overrides: Record = {}): string { describe('TerminalContextMenu', () => { beforeEach(() => { + vi.mocked(translate).mockClear() items.list = [] shortcuts.list = [] vi.stubGlobal('navigator', { userAgent: 'Linux' }) @@ -113,6 +118,16 @@ describe('TerminalContextMenu', () => { vi.unstubAllGlobals() }) + it('does no menu-copy work while closed, then builds the opened menu', () => { + renderMenu({ open: false }) + expect(translate).not.toHaveBeenCalled() + expect(items.list).toHaveLength(0) + + renderMenu() + expect(translate).toHaveBeenCalled() + expect(items.list.length).toBeGreaterThan(0) + }) + it('renders a "Copy Context" item that triggers onCopyAgentSessionContext (issue #5020)', () => { const onCopyAgentSessionContext = vi.fn() const onForkAgentSession = vi.fn() @@ -167,6 +182,7 @@ describe('TerminalContextMenu', () => { item?.onSelect?.() expect(onCopyAgentSessionId).toHaveBeenCalledTimes(1) + vi.mocked(translate).mockClear() items.list = [] renderMenu({ canCopyAgentSessionId: false }) expect( diff --git a/src/renderer/src/components/terminal-pane/TerminalContextMenu.tsx b/src/renderer/src/components/terminal-pane/TerminalContextMenu.tsx index 2cc76cd7164..5236d61d1a6 100644 --- a/src/renderer/src/components/terminal-pane/TerminalContextMenu.tsx +++ b/src/renderer/src/components/terminal-pane/TerminalContextMenu.tsx @@ -78,11 +78,59 @@ type TerminalContextMenuProps = { onCopyAgentSessionId: () => void } -export default function TerminalContextMenu({ - open, +export default function TerminalContextMenu(props: TerminalContextMenuProps): React.JSX.Element { + const { open, onOpenChange, menuPoint, menuOpenedAtRef } = props + return ( + { + if (!nextOpen && Date.now() - menuOpenedAtRef.current < 100) { + return + } + onOpenChange(nextOpen) + }} + modal={false} + > + +