Merge remote-tracking branch 'origin/main' into salvage-20351

This commit is contained in:
Neil
2026-09-12 20:39:46 -07:00
617 changed files with 41818 additions and 6381 deletions
@@ -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
+2 -4
View File
@@ -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
@@ -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:
+2 -1
View File
@@ -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
+27 -1
View File
@@ -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
+2 -1
View File
@@ -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
+2
View File
@@ -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
+4
View File
@@ -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
+15 -14
View File
@@ -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
+15
View File
@@ -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
+4
View File
@@ -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
+8 -8
View File
@@ -54,7 +54,7 @@ Fan one prompt across five agents, each in its own isolated git worktree — com
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="docs/assets/feature-wall/parallel-worktrees.gif" type="image/gif"><img src="docs/assets/feature-wall/parallel-worktrees.jpg" alt="Parallel worktree orchestration" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/model/worktrees"><picture><source srcset="docs/site/public/docs/tab-split.gif" type="image/gif"><img src="docs/site/public/docs/posters/tab-split.jpg" alt="Parallel worktree orchestration" width="100%" /></picture></a>
</td>
</tr>
<tr>
@@ -68,7 +68,7 @@ Ghostty-class terminals with WebGL rendering, infinite splits, and scrollback th
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="docs/assets/feature-wall/terminal-splits.gif" type="image/gif"><img src="docs/assets/feature-wall/terminal-splits.jpg" alt="Terminal splits" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/terminal"><picture><source srcset="resources/onboarding/feature-wall/tile-02.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-02.poster.jpg" alt="Terminal splits" width="100%" /></picture></a>
</td>
</tr>
<tr>
@@ -82,7 +82,7 @@ Click any UI element in a real Chromium window to send its HTML, CSS, and a crop
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="docs/assets/feature-wall/design-mode.gif" type="image/gif"><img src="docs/assets/feature-wall/design-mode.jpg" alt="Embedded browser and Design Mode" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/browser/design-mode"><picture><source srcset="docs/site/public/docs/orca-design-mode.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-05.poster.jpg" alt="Embedded browser and Design Mode" width="100%" /></picture></a>
</td>
</tr>
<tr>
@@ -96,7 +96,7 @@ Browse PRs, issues, and project boards in-app — open a worktree from any task
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="docs/assets/feature-wall/github-linear.gif" type="image/gif"><img src="docs/assets/feature-wall/github-linear.jpg" alt="GitHub and Linear task workflows in Orca" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/review/linear"><picture><source srcset="resources/onboarding/feature-wall/tile-03.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-03.poster.jpg" alt="GitHub and Linear task workflows in Orca" width="100%" /></picture></a>
</td>
</tr>
<tr>
@@ -110,7 +110,7 @@ Run agents on a beefy remote box with full file editing, git, and terminals —
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="docs/assets/feature-wall/ssh-worktrees.gif" type="image/gif"><img src="docs/assets/feature-wall/ssh-worktrees.jpg" alt="Remote worktrees over SSH" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/ssh"><picture><source srcset="resources/onboarding/feature-wall/tile-06.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-06.poster.jpg" alt="Remote worktrees over SSH" width="100%" /></picture></a>
</td>
</tr>
<tr>
@@ -124,7 +124,7 @@ Drop comments on any diff line and ship them back to the agent — review, edit,
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="docs/assets/feature-wall/annotate-diff.gif" type="image/gif"><img src="docs/assets/feature-wall/annotate-diff.jpg" alt="Annotate AI-generated diffs" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/review/annotate-ai-diff"><picture><source srcset="docs/site/public/docs/annotate-ai-diff.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-08.poster.jpg" alt="Annotate AI-generated diffs" width="100%" /></picture></a>
</td>
</tr>
<tr>
@@ -138,7 +138,7 @@ VS Code's editor with autosave everywhere — drag files or images straight into
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="docs/assets/feature-wall/file-drag.gif" type="image/gif"><img src="docs/assets/feature-wall/file-drag.jpg" alt="Drag files and images into an agent prompt" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/editing/file-explorer"><picture><source srcset="resources/onboarding/feature-wall/tile-07.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-07.poster.jpg" alt="Drag files and images into an agent prompt" width="100%" /></picture></a>
</td>
</tr>
<tr>
@@ -152,7 +152,7 @@ Agents drive Orca too — script every workflow with `orca worktree create`, `sn
</td>
<td width="50%">
<a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="docs/assets/feature-wall/orca-cli.gif" type="image/gif"><img src="docs/assets/feature-wall/orca-cli.jpg" alt="Script Orca from the CLI" width="100%" /></picture></a>
<a href="https://www.onorca.dev/docs/cli/overview"><picture><source srcset="resources/onboarding/feature-wall/tile-09.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-09.poster.jpg" alt="Script Orca from the CLI" width="100%" /></picture></a>
</td>
</tr>
</table>
@@ -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)
+2 -1
View File
@@ -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).
+13 -24
View File
@@ -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(
@@ -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)
})
@@ -0,0 +1,58 @@
import type { SqlRow } from './database.js'
type Identity = { userId: string; relayHostId: string }
type RowIndex = Map<string, Map<string, SqlRow[]>>
/** 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
}
@@ -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)
}
)
@@ -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<typeof harness>) {
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<void>()
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(() => {
@@ -111,6 +111,7 @@ function createRegistry(
renewControlActivity: ReturnType<typeof vi.fn>
releaseActivity: ReturnType<typeof vi.fn>
observer: {
recordAuth: ReturnType<typeof vi.fn>
recordControlClose: ReturnType<typeof vi.fn>
recordSpliceClose: ReturnType<typeof vi.fn>
}
@@ -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<K, V>(map: Map<K, V>, 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<K, V>(map: Map<K, V>, counts: LookupCounts): Map<K, V> {
return new Proxy(map, {
get(target, property) {
if (property !== 'values') return bindOwn(target, property)
return function* (): Generator<V> {
for (const value of target.values()) {
counts.visits += 1
yield value
}
}
}
})
}
// One membership check per `pendingConns.has`; answers unchanged.
function countingHas<K, V>(map: Map<K, V>, counts: LookupCounts): Map<K, V> {
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<string, HostSession>,
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<string, unknown>([[`conn-decoy-${index}`, { connId: 'decoy' }]])
return {
relayHostId: `decoy-host-${index}`,
generation: 1,
state: 'active',
activeConnIds: new Set<string>(),
pendingConns: countingHas(pendingConns, counts)
} as unknown as HostSession
}
async function attachRegistry(placement: Placement, store: Partial<RelayCredentialStore> = {}) {
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<string, HostSession> }
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<string, HostSession>()
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)
})
})
+12 -8
View File
@@ -511,16 +511,22 @@ export class HostSessionRegistry {
connTicket: string,
generation: number
): Promise<boolean> {
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<boolean> {
const session = [...this.sessions.values()].find((candidate) =>
candidate.pendingConns.has(connId)
)
const pending = session?.pendingConns.get(connId)
if (
!session ||
@@ -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, number>): number[] {
const values: number[] = new Array<number>(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 <T>(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<Record<string, unknown>> = []
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)
})
})
+17 -5
View File
@@ -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))
}
}
+20 -3
View File
@@ -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 ||
@@ -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)
})
@@ -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,
@@ -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')
+6 -1
View File
@@ -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.
+1 -1
View File
@@ -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 = {
+160 -1
View File
@@ -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",
@@ -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)
)
@@ -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 <ref>: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
)
)
@@ -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 }))
}
+102
View File
@@ -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())
}
@@ -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': [
'<img src="resources/build/icon.png" />',
'<picture><source srcset="docs/site/public/docs/tab-split.gif" type="image/gif"><img src="resources/onboarding/feature-wall/tile-01.poster.jpg" /></picture>',
'<a href="docs/readme/README.ja.md">日本語</a>',
"<img src='resources/build/icon.png' />",
'<img src="https://img.shields.io/badge/x-y-z" />',
'[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': [
'<img src="../../resources/build/icon.png" />',
'<source srcset="../site/public/docs/tab-split.gif">',
'<a href="../../README.md">English</a> <a href="README.ja.md#top">self</a>',
'[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': '<img src="../../../outside.png" />'
})
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<img src='docs/assets/missing.gif' />`
}
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')
})
})
@@ -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'
])
})
})
+97
View File
@@ -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`)
}
}
@@ -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)
+66
View File
@@ -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`
)
}
+124
View File
@@ -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')
})
})
+86
View File
@@ -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`
)
}
File diff suppressed because it is too large Load Diff
+93
View File
@@ -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.
+19
View File
@@ -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)))
}
}
@@ -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')
})
+2 -2
View File
@@ -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')
@@ -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') }`
@@ -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<Method extends RpcMethodName> =
(typeof RPC_PARAMS_BY_METHOD)[Method] extends z.ZodType
? z.output<(typeof RPC_PARAMS_BY_METHOD)[Method]>
@@ -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')
}
}
@@ -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()
})
})
@@ -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', () => {
@@ -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 })
}
@@ -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 })
}
@@ -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)])
)
})
)
}
@@ -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')
@@ -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)
})
})
+4 -1
View File
@@ -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)
}
@@ -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)
})
)
}
@@ -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)
)
@@ -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 <baseline-ref>: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
)
)
@@ -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')
+117 -29
View File
@@ -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 <baseline-ref>')
throw new Error(
'Usage: node config/scripts/mobile-file-ranking-benchmark.mjs <baseline-ref|--autocomplete-stdin>'
)
}
// git show <ref>: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
)
)
@@ -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 <baseline-ref>'
)
}
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
)
)
@@ -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 <baseline-ref>')
}
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)
)
@@ -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)
)
@@ -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 <baseline-ref>'
)
}
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
)
)
@@ -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 <baseline-ref>')
}
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)
)
@@ -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
})
)
}
@@ -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') })
])
)
}
@@ -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 <baseline-ref>')
}
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)
)
@@ -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
)
)
@@ -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 <baseline-ref>'
)
}
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)
)
+1
View File
@@ -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',
@@ -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()
}
@@ -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 <!--metadata--> after <b>text</b>\n'.repeat(100)],
['unclosed-1000', `prefix ${'<!--x'.repeat(1000)}`],
['unclosed-8000', `prefix ${'<!--x'.repeat(8000)} <b>tail</b>`],
['mixed-8000', `prefix <!--complete-->${'<!--x'.repeat(8000)} <b>tail</b>`],
['protected', '\\<!--x `<!--x`\n```html\n<!--x\n```\n'],
['transport', `before [[ORCA_RICH_MD:${key}:inline-html:%3Cb%3E]] and [[README.md]]`]
]) {
for (const htmlSuperscriptLinks of [false, true]) {
const options = { htmlSuperscriptLinks }
const invoke = (arm) =>
arms[arm].encodeRawMarkdownHtmlForRichEditor(input, codecs[arm], options)
assert.equal(invoke('after'), invoke('before'))
const iterations = name.includes('8000') || name.includes('1000') ? 2 : 100
function run(arm) {
global.gc?.()
const start = performance.now()
const cpuStart = process.cpuUsage()
for (let i = 0; i < iterations; i++) {
invoke(arm)
}
const cpu = process.cpuUsage(cpuStart)
return {
ms: (performance.now() - start) / iterations,
cpuMs: (cpu.user + cpu.system) / 1000 / iterations
}
}
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,
htmlSuperscriptLinks,
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)
)
@@ -239,9 +239,9 @@ describe('rebuild-native-deps patched node-pty rebuild', () => {
})
expect(result.status, result.stderr).toBe(0)
expect(result.stdout).toContain('Rebuilding failed native modules: windows-native-registry')
expect(result.stdout).toContain('Rebuilding failed native modules: @orca/windows-registry')
const rebuildCall = JSON.parse(readFileSync(rebuildLogPath, 'utf8').trim())
expect(rebuildCall.onlyModules).toEqual(['windows-native-registry'])
expect(rebuildCall.onlyModules).toEqual(['@orca/windows-registry'])
} finally {
removeTreeSync(projectDir)
}
@@ -358,7 +358,7 @@ exports.loadNativeModule = function loadNativeModule(nativeName) {
}
export function writeFakeWindowsRegistry(projectDir) {
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, 'index.js'),
+2 -4
View File
@@ -76,9 +76,7 @@ if (ignoreModules.length > 0) {
const NATIVE_MODULES = [
'node-pty',
'cpu-features',
...(rebuildPlatform === 'win32'
? ['windows-native-registry', '@vscode/windows-process-tree']
: [])
...(rebuildPlatform === 'win32' ? ['@orca/windows-registry', '@vscode/windows-process-tree'] : [])
]
const onlyModules = NATIVE_MODULES.filter((m) => !ignoreModules.includes(m))
const forceRebuild =
@@ -542,7 +540,7 @@ if (failures.length > 0) {
}
function loadNativeModule(moduleName) {
if (moduleName === 'windows-native-registry') {
if (moduleName === '@orca/windows-registry') {
const registry = projectRequire(moduleName)
// Why: the package defers loading its .node addon until the first registry call.
registry.getRegistryKey(registry.HK.CU, 'Environment')
@@ -0,0 +1,86 @@
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/right-sidebar/pr-comment-fixing-reply-body.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 [name, body] of [
['short', 'Please rename this variable.'],
[
'100-lines',
`<!-- metadata -->\n## Review findings\n${'Code sample with details\n'.repeat(100)}`
],
[
'10000-lines',
`<!-- metadata -->\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)
)
@@ -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',
'<div>\n',
'</div>\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<div>\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 })
}
@@ -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 <placeholder>.'],
['complete', '<!-- metadata --><span>text</span>'],
['unclosed-1000', '<!--x'.repeat(1000)],
['unclosed-8000', '<!--x'.repeat(8000)],
['preserved-html-and-unclosed-8000', `<span>text</span>${'<!--x'.repeat(8000)}<b>tail</b>`]
]) {
assert.equal(
arms.after.getMarkdownRichModeUnsupportedReason(content),
arms.before.getMarkdownRichModeUnsupportedReason(content)
)
const iterations = name.includes('unclosed') ? 2 : 100
function run(arm) {
global.gc?.()
const start = performance.now()
const cpuStart = process.cpuUsage()
for (let i = 0; i < iterations; i++) {
arms[arm].getMarkdownRichModeUnsupportedReason(content)
}
const cpu = process.cpuUsage(cpuStart)
return {
ms: (performance.now() - start) / iterations,
cpuMs: (cpu.user + cpu.system) / 1000 / iterations
}
}
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,
roundTrip:
'identity stub, modeling an already-cached lossless round trip; editor parsing excluded',
results
},
null,
2
)
)
@@ -0,0 +1,90 @@
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 = () => { throw new Error("unexpected editor round trip") }'
: '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', windowsHide: true })
),
after: await load(readFileSync(file, 'utf8'))
}
const results = []
for (const size of [20_000, 200_000, 600_000]) {
for (const shape of ['lines', 'long-line']) {
const phrase =
shape === 'lines'
? 'Ordinary prose with a little `code`.\n'
: 'Ordinary prose with a little `code`. '
const content = phrase.repeat(Math.ceil(size / phrase.length)).slice(0, size)
const invoke = (arm) =>
arms[arm].getMarkdownRichModeEligibilityDecision({ content, sizeOverridden: false })
assert.deepEqual(invoke('after'), invoke('before'))
assert.equal(invoke('after').exceedsSizeLimit, false)
for (let i = 0; i < 10; i++) {
invoke('before')
invoke('after')
}
const samples = { before: [], after: [] }
for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) {
for (const arm of pair) {
global.gc?.()
const cpuStart = process.cpuUsage()
const start = performance.now()
for (let i = 0; i < 20; i++) {
invoke(arm)
}
const ms = (performance.now() - start) / 20
const cpu = process.cpuUsage(cpuStart)
samples[arm].push({ ms, cpuMs: (cpu.user + cpu.system) / 20_000 })
}
}
results.push({ size, shape, samples })
}
}
console.log(
JSON.stringify(
{
baseline,
node: process.version,
roundTrip: 'throwing stub; these inputs must never invoke it',
results
},
null,
2
)
)
@@ -11,6 +11,22 @@ const signalTarget = valueAfter('--signal-target') ?? 'app'
const entrypoint = valueAfter('--entrypoint') ?? 'app'
const intDelivery = valueAfter('--int-delivery') ?? 'foreground-process-group'
const launcherExecOverlay = args.includes('--launcher-exec-overlay')
const allEntrypoints = args.includes('--all-entrypoints')
if (
allEntrypoints &&
['--entrypoint', '--signal-target', '--int-delivery', '--launcher-exec-overlay'].some((flag) =>
args.includes(flag)
)
) {
fail('--all-entrypoints cannot be combined with individual case options')
}
const cases = allEntrypoints
? [
{ entrypoint: 'app', signalTarget: 'app', intDelivery: 'foreground-process-group' },
{ entrypoint: 'launcher', signalTarget: 'app', intDelivery: 'foreground-process-group' },
{ entrypoint: 'appimage', signalTarget: 'serving-electron', intDelivery: 'pid' }
]
: [{ entrypoint, signalTarget, intDelivery }]
if (!appImageArg) {
fail('Usage: run-headless-serve-shutdown-docker.mjs --appimage /path/to/orca.AppImage')
}
@@ -98,50 +114,52 @@ try {
].join(' && ')
])
console.log(
JSON.stringify({
type: 'appimage_under_test',
appImage,
sha256,
platform,
signalTarget,
entrypoint,
intDelivery,
launcherExecOverlay
})
)
const failedSignals = []
for (const signal of ['INT', 'TERM']) {
const result = docker(
[
'run',
'--rm',
'--init',
'--platform',
for (const { entrypoint, signalTarget, intDelivery } of cases) {
console.log(
JSON.stringify({
type: 'appimage_under_test',
appImage,
sha256,
platform,
'--shm-size',
'256m',
'--name',
`orca-headless-serve-shutdown-${signal.toLowerCase()}-${suffix}`,
'-e',
`ORCA_SIGNAL_TARGET=${signalTarget}`,
'-e',
`ORCA_TEST_ENTRYPOINT=${entrypoint}`,
'-e',
`ORCA_INT_DELIVERY=${intDelivery}`,
'-v',
`${appImage}:/input/orca.AppImage:ro`,
'-v',
`${artifactVolume}:/artifacts:ro`,
image,
signal
],
{ allowFailure: true }
signalTarget,
entrypoint,
intDelivery,
launcherExecOverlay
})
)
process.stdout.write(result.stdout)
process.stderr.write(result.stderr)
if (result.status !== 0) {
failedSignals.push(`${signal}:${result.status}`)
for (const signal of ['INT', 'TERM']) {
const result = docker(
[
'run',
'--rm',
'--init',
'--platform',
platform,
'--shm-size',
'256m',
'--name',
`orca-headless-serve-shutdown-${entrypoint}-${signal.toLowerCase()}-${suffix}`,
'-e',
`ORCA_SIGNAL_TARGET=${signalTarget}`,
'-e',
`ORCA_TEST_ENTRYPOINT=${entrypoint}`,
'-e',
`ORCA_INT_DELIVERY=${intDelivery}`,
'-v',
`${appImage}:/input/orca.AppImage:ro`,
'-v',
`${artifactVolume}:/artifacts:ro`,
image,
signal
],
{ allowFailure: true }
)
process.stdout.write(result.stdout)
process.stderr.write(result.stderr)
if (result.status !== 0) {
failedSignals.push(`${entrypoint}:${signal}:${result.status}`)
}
}
}
if (failedSignals.length > 0) {
@@ -0,0 +1,134 @@
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 <ref>:src/main/emulator/android/scrcpy-video-frame-parser.ts | node config/scripts/scrcpy-frame-buffering-benchmark.mjs
async function load(source) {
const { code } = await transform(source, { loader: 'ts', format: 'esm' })
return import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`)
}
const before = (await load(readFileSync(0, 'utf8'))).parseScrcpyVideoFrames
const after = (
await load(readFileSync('src/main/emulator/android/scrcpy-video-frame-parser.ts', 'utf8'))
).parseScrcpyVideoFrames
const { RelayFrameBuffer } = await load(readFileSync('src/shared/relay-frame-buffer.ts', 'utf8'))
function reader(arm) {
if (arm === 'before') {
let pending = Buffer.alloc(0)
return {
read(chunk) {
// Match the baseline session's copy before invoking its production parser.
const result = before(Buffer.alloc(0), Buffer.concat([pending, chunk]))
pending = result.pending
return result.frames
},
pending: () => pending
}
}
const pending = new RelayFrameBuffer()
return {
read(chunk) {
// Include the session's mandatory ownership copy in the queued parser arm.
if (chunk.length > 0) {
pending.append(Buffer.from(chunk))
}
return after(pending)
},
pending: () => (pending.length > 0 ? pending.peek(pending.length) : Buffer.alloc(0))
}
}
function packet(size, meta = 123n) {
const frame = Buffer.alloc(size + 12, 7)
frame.writeBigUInt64BE(meta, 0)
frame.writeUInt32BE(size, 8)
return frame
}
let seed = 42
const random = (max) => {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return seed % max
}
let differentialChunks = 0
for (let trial = 0; trial < 1000; trial += 1) {
const stream = Buffer.concat(
Array.from({ length: 1 + random(8) }, (_, index) =>
packet(random(256), (BigInt(random(4)) << 62n) | BigInt(index))
)
)
const oldReader = reader('before')
const newReader = reader('after')
for (let offset = 0; offset < stream.length;) {
const size = 1 + random(128)
const chunk = stream.subarray(offset, offset + size)
assert.deepEqual(newReader.read(chunk), oldReader.read(chunk))
assert.deepEqual(newReader.pending(), oldReader.pending())
assert.deepEqual(newReader.read(Buffer.alloc(0)), oldReader.read(Buffer.alloc(0)))
differentialChunks += 1
offset += size
}
}
const results = []
for (const frameBytes of [32, 4096, 65_536, 1_048_576]) {
const frame = packet(frameBytes)
const expected = reader('before').read(frame)
for (const chunkBytes of new Set([frame.length, 65_536, 4096, 1024])) {
if (chunkBytes > frame.length) {
continue
}
const chunks = []
for (let offset = 0; offset < frame.length; offset += chunkBytes) {
chunks.push(frame.subarray(offset, offset + chunkBytes))
}
const iterations = Math.max(10, Math.floor(4_194_304 / frameBytes))
function run(arm, repeats) {
const parser = reader(arm)
let frames
const started = performance.now()
for (let iteration = 0; iteration < repeats; iteration += 1) {
for (const chunk of chunks) {
frames = parser.read(chunk)
}
}
const ms = performance.now() - started
assert.deepEqual(frames, expected)
assert.equal(parser.pending().length, 0)
return ms
}
run('before', iterations)
run('after', iterations)
/** @type {{ before: number[], after: number[] }} */
const samples = { before: [], after: [] }
for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) {
for (const arm of pair) {
samples[arm].push(run(arm, iterations))
}
}
results.push({
frameBytes,
chunkBytes,
iterations,
meanMicrosecondsPerFrame: 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, differentialChunks, results },
null,
2
)
)
@@ -0,0 +1,188 @@
import assert from 'node:assert/strict'
import { performance } from 'node:perf_hooks'
import { build } from 'esbuild'
import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs'
const bundled = await build({
stdin: {
contents: "export * from './src/main/ai-vault/codex-session-root-dedup.ts'",
resolveDir: process.cwd(),
loader: 'ts'
},
bundle: true,
platform: 'node',
format: 'esm',
write: false
})
const production = await import(
`data:text/javascript;base64,${Buffer.from(bundled.outputFiles[0].text).toString('base64')}`
)
function baseline(input) {
const sessions = []
for (let offset = 0; offset < input.length; offset += 8) {
sessions.push(...input.slice(offset, offset + 8))
const unique = production.dedupeCodexSessionsBySessionId(sessions)
sessions.splice(0, sessions.length, ...unique)
}
return sessions
}
function incremental(input) {
const sessions = new production.CodexSessionCollection()
for (let offset = 0; offset < input.length; offset += 8) {
for (const session of input.slice(offset, offset + 8)) {
sessions.add(session)
}
}
return [...sessions.values()]
}
function makeSession(index, overrides = {}) {
return Object.freeze({
agent: 'codex',
executionHostId: 'local',
sessionId: `session-${index}`,
filePath: `/home/ada/.codex/sessions/2026/09/11/rollout-session-${index}.jsonl`,
codexHome: null,
updatedAt: '2026-09-11T10:00:00.000Z',
createdAt: null,
modifiedAt: '2026-09-11T10:00:00.000Z',
...overrides
})
}
function checkIdentities(actual, expected) {
assert.equal(actual.length, expected.length)
actual.forEach((session, index) => assert.equal(session, expected[index]))
}
let seed = 90211
function random(max) {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return Math.floor((seed / 0x100000000) * max)
}
if (production.CodexSessionCollection) {
let batches = 0
for (let trial = 0; trial < 2000; trial++) {
const input = []
const current = new production.CodexSessionCollection()
let expected = []
for (let batch = 0; batch < 20; batch++) {
const added = Array.from({ length: 1 + random(8) }, () => {
if (input.length && random(4) === 0) {
return input[random(input.length)]
}
const index = random(12)
const root = [
'/home/ada/.codex',
'/tmp/codex-runtime-home/home',
'/tmp/codex-accounts/account/home',
'/tmp/custom',
'\\\\wsl$\\Ubuntu\\home\\ada\\.codex',
'\\\\wsl.localhost\\ubuntu\\home\\ada\\.codex',
'\\\\wsl$\\Debian\\home\\ada\\.codex',
'C:\\Users\\Ada\\.codex'
][random(8)]
return makeSession(index, {
agent: random(6) ? 'codex' : 'claude',
executionHostId: random(5) ? 'local' : 'ssh:dev',
sessionId: `session-${random(3)}`,
codexHome: random(4) ? root : null,
filePath: `${root}/sessions/${random(6) ? 'rollout-' : ''}${index}.jsonl`,
updatedAt: [null, 'invalid', '2026-09-11T10:00:00Z', '2026-09-11T10:01:00Z'][random(4)],
modifiedAt: random(4) ? '2026-09-11T10:00:00Z' : 'invalid'
})
})
input.push(...added)
expected = production.dedupeCodexSessionsBySessionId([...expected, ...added])
added.forEach((session) => current.add(session))
checkIdentities([...current.values()], expected)
assert.equal(current.size, expected.length)
batches++
}
}
console.log(JSON.stringify({ differentialBatches: batches }))
}
const workloads = []
if (process.argv.includes('--verify-only')) {
process.exit(0)
}
for (const count of [8, 100, 1000, 5000, 10000]) {
workloads.push([`${count} unique Codex`, Array.from({ length: count }, (_, i) => makeSession(i))])
workloads.push([
`${count} Claude`,
Array.from({ length: count }, (_, i) => makeSession(i, { agent: 'claude' }))
])
}
for (const count of [1000, 5000]) {
const input = Array.from({ length: count }, (_, i) => makeSession(i))
const aliases = input.map((session) =>
makeSession(0, {
...session,
codexHome: '/tmp/custom',
filePath: session.filePath.replace('/home/ada/.codex', '/tmp/custom')
})
)
workloads.push([`${count} late preferred roots`, [...aliases, ...input]])
workloads.push([`${count} late losing roots`, [...input, ...aliases]])
}
function median(samples) {
const sorted = [...samples].sort((a, b) => a - b)
const mid = Math.floor(sorted.length / 2)
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2
}
console.log(
JSON.stringify({ node: process.version, platform: process.platform, arch: process.arch })
)
for (const [name, input] of workloads) {
const expected = baseline(input)
const arms = { baseline, ...(production.CodexSessionCollection ? { incremental } : {}) }
const repeats = Math.max(1, Math.floor(5000 / input.length))
const samples = { baseline: [], incremental: [] }
for (const run of Object.values(arms)) {
checkIdentities(run(input), expected)
}
for (const pair of buildCounterbalancedSchedule(8, 'baseline', 'incremental')) {
for (const arm of pair) {
if (!arms[arm]) {
continue
}
const start = performance.now()
let result
for (let repeat = 0; repeat < repeats; repeat++) {
result = arms[arm](input)
}
samples[arm].push((performance.now() - start) / repeats)
checkIdentities(result, expected)
}
}
console.log(
JSON.stringify({
name,
medianMs: Object.fromEntries(
Object.entries(samples)
.filter(([, values]) => values.length)
.map(([arm, values]) => [arm, median(values)])
)
})
)
}
if (global.gc && production.CodexSessionCollection) {
for (const count of [1000, 10000]) {
const input = Array.from({ length: count }, (_, index) => makeSession(index))
global.gc()
const before = process.memoryUsage().heapUsed
const collection = new production.CodexSessionCollection()
input.forEach((session) => collection.add(session))
global.gc()
const retainedBytes = process.memoryUsage().heapUsed - before
checkIdentities([...collection.values()], input)
console.log(JSON.stringify({ name: `${count} scan-local index`, retainedBytes }))
}
}
@@ -0,0 +1,197 @@
#!/usr/bin/env node
// git show <base>:src/main/ai-vault/session-scanner.ts | node config/scripts/session-scan-cutoff-benchmark.mjs
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { performance } from 'node:perf_hooks'
import ts from 'typescript-api'
import { build } from 'esbuild'
import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs'
const baselineSource = ts.createSourceFile(
'session-scanner.ts',
readFileSync(0, 'utf8'),
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TS
)
const baselineFunction = baselineSource.statements.find(
(node) => ts.isFunctionDeclaration(node) && node.name?.text === 'canStopParsingSessions'
)
assert(baselineFunction, 'Pipe the baseline session-scanner.ts on stdin')
async function load(contents) {
const result = await build({
stdin: { contents, resolveDir: path.resolve('src/main/ai-vault'), loader: 'ts' },
platform: 'node',
format: 'esm',
bundle: true,
write: false
})
const encoded = Buffer.from(result.outputFiles[0].text).toString('base64')
return import(`data:text/javascript;base64,${encoded}`)
}
const [baselineModule, currentModule] = await Promise.all([
load(`import { sessionSortTime } from './session-scanner-accumulator';
export ${baselineFunction.getText(baselineSource)}`),
load(`export { canStopParsingSessions } from './session-scan-cutoff';
export { CodexSessionCollection } from './codex-session-root-dedup';`)
])
const baseline = baselineModule.canStopParsingSessions
const current = currentModule.canStopParsingSessions
const { CodexSessionCollection } = currentModule
let randomState = 91114
function random(bound) {
randomState = (Math.imul(randomState, 1664525) + 1013904223) >>> 0
return Math.floor((randomState / 2 ** 32) * bound)
}
function session(index, overrides = {}) {
return Object.freeze({
agent: 'claude',
executionHostId: 'local',
sessionId: `session-${index}`,
filePath: `/home/ada/.codex/sessions/rollout-${index}.jsonl`,
codexHome: null,
updatedAt: new Date(index).toISOString(),
modifiedAt: new Date(0).toISOString(),
...overrides
})
}
function collection(rows) {
const result = new CodexSessionCollection()
for (const row of rows) {
result.add(row)
}
return result
}
function check(sessions, limit, next) {
const rows = [...sessions.values()]
assert.equal(current(sessions, limit, next), baseline(sessions, limit, next))
assert.deepEqual([...sessions.values()], rows)
}
const dates = [
null,
'',
'invalid',
'1970-01-01T00:00:00Z',
'1970-01-01T00:00:02+00:00',
'-000001-01-01T00:00:00Z',
'+010000-01-01T00:00:00Z',
'-271821-04-20T00:00:00.000Z'
]
const limits = [0, -1, -3, 0.5, 1.5, Number.NaN, Infinity, -Infinity]
const nextTimes = [undefined, Number.NaN, Infinity, -Infinity, 0, 1, 2, 2000]
let comparisons = 0
for (let trial = 0; trial < 4_000; trial += 1) {
const sessions = new CodexSessionCollection()
const admitted = []
for (let batch = 0; batch < 10; batch += 1) {
const count = random(8)
for (let index = 0; index < count; index += 1) {
const id = random(12)
const row =
admitted.length && random(5) === 0
? admitted[random(admitted.length)]
: session(id, {
agent: random(3) ? 'codex' : 'claude',
executionHostId: random(4) ? 'local' : 'ssh:dev',
codexHome: random(2) ? null : '/custom',
updatedAt: random(3)
? new Date(random(5000) - 2500).toISOString()
: dates[random(dates.length)],
modifiedAt: random(4) ? new Date(random(5000)).toISOString() : 'invalid'
})
admitted.push(row)
sessions.add(row)
}
const limit = random(3) ? 1 + random(40) : limits[random(limits.length)]
const next = random(2) ? random(5000) - 2500 : nextTimes[random(nextTimes.length)]
check(sessions, limit, next)
comparisons += 1
}
}
console.log(`${comparisons} differential batch cutoffs passed.`)
function median(values) {
const sorted = values.toSorted((left, right) => left - right)
return (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2
}
function measure(name, run, repeats) {
const expected = run(baseline)
assert.deepEqual(run(current), expected)
const sample = (cutoff) => {
let result
const start = performance.now()
for (let index = 0; index < repeats; index += 1) {
result = run(cutoff)
}
const elapsed = (performance.now() - start) / repeats
assert.deepEqual(result, expected)
return elapsed
}
sample(baseline)
sample(current)
const samples = { baseline: [], current: [] }
for (const pair of buildCounterbalancedSchedule(8, 'baseline', 'current')) {
for (const arm of pair) {
samples[arm].push(sample(arm === 'baseline' ? baseline : current))
}
}
return { name, beforeMs: median(samples.baseline), afterMs: median(samples.current) }
}
console.log(
JSON.stringify({ node: process.version, platform: process.platform, arch: process.arch })
)
const results = []
for (const count of [8, 100, 1_000, 2_000, 10_000]) {
for (const order of ['ordered', 'shuffled']) {
const rows = Array.from({ length: count }, (_, index) => session(count - index))
if (order === 'shuffled') {
for (let index = count - 1; index > 0; index -= 1) {
const other = random(index + 1)
;[rows[index], rows[other]] = [rows[other], rows[index]]
}
}
const sessions = collection(rows)
for (const next of [0, count]) {
results.push(
measure(
`${count} ${order} / ${next === 0 ? 'stop' : 'continue'}`,
(cutoff) => cutoff(sessions, Math.ceil(count / 2), next),
Math.max(20, Math.floor(30_000 / count))
)
)
}
}
}
for (const invalidIndex of [0, 999]) {
const sessions = collection(
Array.from({ length: 1_000 }, (_, index) =>
session(index, invalidIndex === index ? { updatedAt: 'invalid' } : {})
)
)
results.push(measure(`1000 invalid at ${invalidIndex}`, (cutoff) => cutoff(sessions, 500, 0), 50))
}
const rows = Array.from({ length: 2_000 }, () => session(random(2_000)))
results.push(
measure(
'2000-candidate scan cutoff + admission / limit1000',
(cutoff) => {
const sessions = new CodexSessionCollection()
let index = 0
while (index < rows.length && !cutoff(sessions, 1_000, 10_000)) {
const end = Math.min(rows.length, index + Math.min(8, Math.max(1, 1_000 - sessions.size)))
while (index < end) {
sessions.add(rows[index++])
}
}
return { parsed: index, sessions: sessions.size }
},
2
)
)
console.table(results)
console.log('Synthetic cutoff/admission CPU; excludes discovery, parsing, I/O and final sorting.')
@@ -0,0 +1,192 @@
import { rm, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import {
createSessionParseStats,
parseAgentSessionFileCached,
resetSessionParseCacheForTests
} from '../../src/main/ai-vault/session-scanner-parse-cache'
import { resetTranscriptConsumersForTests } from '../../src/main/ai-vault/session-transcript-consumers'
import { SessionSearchEngine } from '../../src/main/ai-vault-search/session-search-engine'
import type {
SessionSearchRequest,
SessionSearchScope
} from '../../src/main/ai-vault-search/session-search-engine-types'
import { registerSessionSearchIndexConsumer } from '../../src/main/ai-vault-search/session-search-index-consumer'
import { SessionSearchStore } from '../../src/main/ai-vault-search/session-search-store'
import type SyncDatabase from '../../src/main/sqlite/sync-database'
import {
writeSyntheticTranscriptCorpus,
type SyntheticCorpus,
type SyntheticCorpusOptions
} from '../../src/main/ai-vault-search/session-search-synthetic-corpus'
import { sessionCandidate } from '../../src/main/ai-vault-search/session-search-transcript-fixtures'
// What a query costs, and what the session candidate limit buys. Everything
// runs through the real store and the real engine over a synthetic corpus;
// never point this at a real transcript tree.
const WARMUP = 5
const SAMPLES = 25
// One query per rung the ladder can take, plus the two shapes that skip it.
const QUERIES: { name: string; request: SessionSearchRequest }[] = [
{ name: 'phrase', request: { query: '"terminal reattach"' } },
{ name: 'identifier', request: { query: 'resolveTerminalPath' } },
{ name: 'path', request: { query: 'src/main/ai-vault/session-transcript-reader.ts' } },
{ name: 'prose', request: { query: 'why is the daemon snapshot stale' } },
{ name: 'typo', request: { query: 'reattahc worktre' } },
{ name: 'common-term', request: { query: 'index' } },
{ name: 'operator-only', request: { query: 'repo:app-3' } },
{ name: 'scoped', request: { query: 'worktree', filters: { scopePaths: ['/repo/app-3'] } } }
]
type Timing = { p50: number; p95: number }
function percentile(sorted: readonly number[], fraction: number): number {
const at = Math.min(sorted.length - 1, Math.floor(sorted.length * fraction))
return Math.round((sorted[at] ?? 0) * 100) / 100
}
function timing(samples: number[]): Timing {
const sorted = [...samples].sort((left, right) => left - right)
return { p50: percentile(sorted, 0.5), p95: percentile(sorted, 0.95) }
}
function time(engine: SessionSearchEngine, request: SessionSearchRequest): number {
const started = performance.now()
engine.search(request)
return performance.now() - started
}
async function indexCorpus(
options: SyntheticCorpusOptions
): Promise<{ corpus: SyntheticCorpus; db: SyncDatabase; release: () => void }> {
resetSessionParseCacheForTests()
const corpus = await writeSyntheticTranscriptCorpus(options)
const store = new SessionSearchStore(join(corpus.root, 'index.sqlite'), (error) => {
throw error
})
const unregister = registerSessionSearchIndexConsumer(store)
const stats = createSessionParseStats()
for (const path of corpus.files) {
await parseAgentSessionFileCached(
await sessionCandidate('claude', path),
process.platform,
stats
)
}
return {
corpus,
// The handle a composed reader gets. Every read here is one synchronous
// statement, which is the contract that comes with it.
db: store.connection,
release: () => {
unregister()
resetTranscriptConsumersForTests()
resetSessionParseCacheForTests()
store.close()
}
}
}
/** Per-query and overall latency for one scope. */
function scopeReport(db: SyncDatabase, scope: SessionSearchScope): Record<string, unknown> {
const engine = new SessionSearchEngine(db)
const everything: number[] = []
const perQuery: Record<string, Timing & { hits: number; route: string }> = {}
for (const { name, request } of QUERIES) {
const scoped = { ...request, scope }
for (let run = 0; run < WARMUP; run++) {
engine.search(scoped)
}
const samples = Array.from({ length: SAMPLES }, () => time(engine, scoped))
everything.push(...samples)
const result = engine.search(scoped)
perQuery[name] = { ...timing(samples), hits: result.hits.length, route: result.planner.route }
}
return { ...timing(everything), perQuery }
}
/**
* The candidate limit only costs anything once there are more matching sessions
* than the limit, so this runs over many short sessions rather than the wide
* corpus above. Limits are interleaved sample by sample: run back to back, the
* first configuration pays for every page the OS cache had not seen yet and the
* ordering alone moves p95 by more than the limit does.
*/
function candidateSweep(db: SyncDatabase, limits: readonly number[]): Record<string, unknown> {
const request: SessionSearchRequest = { query: 'index', limit: 20 }
const engines = new Map(
limits.map((limit) => [limit, new SessionSearchEngine(db, { sessionCandidateLimit: limit })])
)
const samples = new Map(limits.map((limit) => [limit, [] as number[]]))
for (let run = 0; run < WARMUP; run++) {
for (const engine of engines.values()) {
engine.search(request)
}
}
for (let run = 0; run < SAMPLES; run++) {
for (const limit of limits) {
samples.get(limit)!.push(time(engines.get(limit)!, request))
}
}
const report: Record<string, unknown> = {}
for (const limit of limits) {
const result = engines.get(limit)!.search(request)
report[String(limit)] = {
...timing(samples.get(limit)!),
truncated: result.truncated.candidates,
// Pages a caller could walk before the limit stops handing out sessions.
reachablePages: Math.ceil(limit / (request.limit ?? 20))
}
}
return report
}
const wide = await indexCorpus({ sessions: Number(process.env.SESSIONS ?? 40) })
let report: string
try {
const scope = {
all: scopeReport(wide.db, 'all'),
conversation: scopeReport(wide.db, 'conversation')
}
wide.release()
await rm(wide.corpus.root, { recursive: true, force: true })
// Many short sessions: what makes the candidate limit binding is the session
// count, not the byte count.
const many = await indexCorpus({ sessions: 2500, turnsPerSession: 1, seed: 7 })
try {
report = JSON.stringify(
{
scopeCorpus: {
sessions: wide.corpus.files.length,
transcriptMb: Math.round((wide.corpus.transcriptBytes / 1024 / 1024) * 100) / 100,
messages: wide.corpus.messageCount
},
scope,
candidateCorpus: {
sessions: many.corpus.files.length,
transcriptMb: Math.round((many.corpus.transcriptBytes / 1024 / 1024) * 100) / 100
},
candidateSweep: candidateSweep(many.db, [200, 600, 1200, 2400])
},
null,
2
)
} finally {
many.release()
await rm(many.corpus.root, { recursive: true, force: true })
}
} catch (error) {
await rm(wide.corpus.root, { recursive: true, force: true })
throw error
}
// Why a file as well as stdout: a runner that intercepts console output
// (vitest does) would otherwise swallow the whole report.
const out = process.env.BENCH_OUT
if (out) {
await writeFile(out, `${report}\n`)
}
console.log(report)
@@ -0,0 +1,205 @@
import { rm, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import {
createSessionParseStats,
parseAgentSessionFileCached,
resetSessionParseCacheForTests
} from '../../src/main/ai-vault/session-scanner-parse-cache'
import { resetTranscriptConsumersForTests } from '../../src/main/ai-vault/session-transcript-consumers'
import { SessionSearchEngine } from '../../src/main/ai-vault-search/session-search-engine'
import type {
SessionSearchRequest,
SessionSearchScope
} from '../../src/main/ai-vault-search/session-search-engine-types'
import { registerSessionSearchIndexConsumer } from '../../src/main/ai-vault-search/session-search-index-consumer'
import { SessionSearchStore } from '../../src/main/ai-vault-search/session-search-store'
import { sessionCandidate } from '../../src/main/ai-vault-search/session-search-transcript-fixtures'
import type SyncDatabase from '../../src/main/sqlite/sync-database'
import { writeToolHeavyCorpus, type ToolHeavyCorpus } from './session-search-tool-heavy-corpus'
// What each scope costs on an index the size of a real transcript tree.
//
// The 10.5 MB corpus in `session-search-query-benchmark.ts` sizes the route
// ladder; this one sizes the corpus. `conversation` is a column filter over the
// one FTS table rather than a second table of its own, and the whole cost of
// that decision is how much of `messages_fts` a conversation query has to read
// past — which is set by how much of a transcript is tool output.
//
// Synthetic, always: this must never be pointed at a real transcript.
const WARMUP = 5
/** Conversation-shaped queries; every term is one the prose actually uses. */
const QUERIES = [
'terminal reattach',
'stale snapshot',
'daemon cursor',
'worktree index',
'publish transaction',
'relay daemon',
'session cursor',
'because stale',
'terminal worktree',
'index snapshot',
'reattach cursor',
'transaction relay',
'snapshot session',
'daemon publish',
'worktree terminal',
'cursor index',
'stale relay',
'session transaction',
'publish snapshot',
'reattach daemon'
]
async function indexCorpus(
corpus: ToolHeavyCorpus
): Promise<{ db: SyncDatabase; release: () => void }> {
resetSessionParseCacheForTests()
const store = new SessionSearchStore(join(corpus.root, 'index.sqlite'), (error) => {
throw error
})
const unregister = registerSessionSearchIndexConsumer(store)
const stats = createSessionParseStats()
for (const path of corpus.files) {
await parseAgentSessionFileCached(
await sessionCandidate('claude', path),
process.platform,
stats
)
}
return {
// The store's own handle, which is what a composed reader gets: every
// retrieval is one synchronous statement, so nothing pins a WAL snapshot.
db: store.connection,
release: () => {
unregister()
resetTranscriptConsumersForTests()
resetSessionParseCacheForTests()
store.close()
}
}
}
type Timing = { p50: number; p95: number }
function timing(samples: readonly number[]): Timing {
const sorted = [...samples].sort((left, right) => left - right)
const at = (fraction: number): number => {
const index = Math.min(sorted.length - 1, Math.floor(sorted.length * fraction))
return Math.round((sorted[index] ?? 0) * 100) / 100
}
return { p50: at(0.5), p95: at(0.95) }
}
/**
* The query sets, one per rung of the ladder the engine may take.
*
* Which rung each one reaches is not forced, it is observed: samples are
* bucketed by the route the engine reports, so the table says what was measured
* rather than what was intended, and a query that lands on a different rung
* than expected shows up as a bucket rather than as a wrong number.
*/
function queries(): string[] {
const run = (index: number, length: number): string =>
Array.from({ length }, (_unused, step) => QUERIES[(index + step) % QUERIES.length]).join(' ')
return [
// Two terms, unquoted: not literal, so straight to OR.
...QUERIES,
// Two terms, quoted: literal, and on this corpus any two of fourteen words
// sit next to each other somewhere, so the phrase rung answers.
...QUERIES.map((query) => `"${query}"`),
// Eight terms, quoted: an ordered run that long does not occur in 105 MB of
// draws from fourteen words, so the phrase rung misses and AND answers.
...QUERIES.map((_query, index) => `"${run(index, 4)}"`)
]
}
type Bucket = { samples: number[]; hits: number }
/**
* Both scopes over the same queries, interleaved scope by scope: run back to
* back, the first one pays for every page the OS cache had not seen and the
* ordering moves p95 more than the scope does.
*/
function scopeReport(db: SyncDatabase): Record<string, unknown> {
const engine = new SessionSearchEngine(db)
const scopes: SessionSearchScope[] = ['all', 'conversation']
const requests: SessionSearchRequest[] = queries().map((query) => ({ query }))
const buckets = new Map<string, Bucket>()
for (let run = 0; run < WARMUP; run++) {
for (const scope of scopes) {
for (const request of requests) {
engine.search({ ...request, scope })
}
}
}
for (const request of requests) {
for (const scope of scopes) {
const started = performance.now()
const result = engine.search({ ...request, scope })
const elapsed = performance.now() - started
const key = `${result.planner.route}/${scope}`
const bucket = buckets.get(key) ?? { samples: [], hits: 0 }
bucket.samples.push(elapsed)
bucket.hits += result.hits.length
buckets.set(key, bucket)
}
}
const report: Record<string, unknown> = {}
for (const [key, bucket] of [...buckets].sort(([left], [right]) => left.localeCompare(right))) {
report[key] = { ...timing(bucket.samples), samples: bucket.samples.length, hits: bucket.hits }
}
return report
}
/** Bytes the FTS table occupies, which is the cost the deleted second table saved. */
function indexBytes(db: SyncDatabase): Record<string, number> | { unavailable: string } {
try {
const sum = (where: string, ...values: string[]): number =>
Number(
(
db
.prepare(`SELECT COALESCE(SUM(pgsize),0) AS bytes FROM dbstat ${where}`)
.get(...values) as { bytes: number }
).bytes
)
return { total: sum(''), messagesFts: sum('WHERE name LIKE ?', 'messages_fts%') }
} catch {
// dbstat is a compile-time option; the latency numbers stand without it.
return { unavailable: 'no dbstat' }
}
}
const corpus = await writeToolHeavyCorpus({
targetBytes: Number(process.env.CORPUS_MB ?? 100) * 1024 * 1024,
toolShare: Number(process.env.TOOL_SHARE ?? 0.9)
})
let report: string
const indexed = await indexCorpus(corpus)
try {
report = JSON.stringify(
{
corpus: {
sessions: corpus.files.length,
transcriptMb: Math.round((corpus.transcriptBytes / 1024 / 1024) * 100) / 100,
toolShareOfMessageText:
Math.round((corpus.toolBytes / (corpus.toolBytes + corpus.proseBytes)) * 1000) / 1000
},
indexBytes: indexBytes(indexed.db),
route: scopeReport(indexed.db)
},
null,
2
)
} finally {
indexed.release()
await rm(corpus.root, { recursive: true, force: true })
}
const out = process.env.BENCH_OUT
if (out) {
await writeFile(out, `${report}\n`)
}
console.log(report)
@@ -0,0 +1,152 @@
import { mkdtemp, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
// The corpus the scope benchmark runs over. Written here rather than by
// `session-search-synthetic-corpus.ts` because what it costs to answer a
// conversation query out of the one FTS table turns on the property that
// generator fixes: how much of a transcript is tool output.
//
// Synthetic, always. This must never be pointed at a real transcript.
const PROSE = [
'terminal',
'reattach',
'worktree',
'the',
'index',
'cursor',
'publish',
'transaction',
'relay',
'daemon',
'snapshot',
'because',
'stale',
'session'
]
// Tool output is paths, hashes and log lines — and the same words the
// conversation uses, because a `rg` over this repository prints them. That
// overlap is what the benchmark turns on: it is what makes a conversation
// term's posting list carry rows the column filter then has to discard. A tool
// vocabulary disjoint from the prose would leave nothing to discard and measure
// the wrong thing.
const TOOL_ONLY = [
'src/main/ai-vault/session-transcript-reader.ts',
'node_modules/.pnpm/typescript@5.9.2',
'0x00007ff8',
'ENOENT',
'drwxr-xr-x',
'2026-09-10T00:00:00.000Z',
'sha256:9f2c1a',
'chunk-VHQ4NWQK.js',
'warning:',
'resolveTerminalPath',
'byteOffset',
'MAX_RETRIES'
]
// Half the tool tokens are conversation words. Deliberately pessimistic: the
// more of a query term lives in `tool_text`, the more the column filter costs,
// so a number measured here holds on a real transcript tree.
const TOOL = [...PROSE, ...TOOL_ONLY]
function mulberry32(seed: number): () => number {
let state = seed >>> 0
return () => {
state = (state + 0x6d2b79f5) >>> 0
let t = Math.imul(state ^ (state >>> 15), 1 | state)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
function words(random: () => number, vocabulary: readonly string[], count: number): string {
const out: string[] = []
for (let index = 0; index < count; index++) {
out.push(vocabulary[Math.floor(random() * vocabulary.length)]!)
}
return out.join(' ')
}
export type ToolHeavyCorpus = {
root: string
files: string[]
transcriptBytes: number
toolBytes: number
proseBytes: number
}
/**
* Claude JSONL transcripts whose tool output is `toolShare` of the message text.
* One turn is a user question, an assistant answer, a tool call and its output;
* only the last one grows with the share.
*/
export async function writeToolHeavyCorpus(args: {
targetBytes: number
toolShare: number
seed?: number
}): Promise<ToolHeavyCorpus> {
const random = mulberry32(args.seed ?? 11)
const root = await mkdtemp(join(tmpdir(), 'orca-search-convfts-'))
const files: string[] = []
const proseWordsPerTurn = 160
// Tool and prose words are not the same length, so the share is over bytes.
const proseBytesPerTurn = proseWordsPerTurn * 6
const toolWordCount = Math.max(
1,
Math.round((proseBytesPerTurn * args.toolShare) / (1 - args.toolShare) / 22)
)
let transcriptBytes = 0
let toolBytes = 0
let proseBytes = 0
for (let session = 0; transcriptBytes < args.targetBytes; session++) {
const sessionId = `00000000-0000-4000-8000-${String(session).padStart(12, '0')}`
const lines: string[] = []
for (let turn = 0; turn < 40; turn++) {
const at = new Date(1740000000000 + turn * 60_000).toISOString()
const question = words(random, PROSE, 40)
const answer = words(random, PROSE, proseWordsPerTurn - 40)
const output = words(random, TOOL, toolWordCount)
proseBytes += Buffer.byteLength(question) + Buffer.byteLength(answer)
toolBytes += Buffer.byteLength(output)
lines.push(
JSON.stringify({
type: 'user',
sessionId,
timestamp: at,
cwd: `/repo/app-${session % 7}`,
gitBranch: 'main',
message: { role: 'user', content: question }
}),
JSON.stringify({
type: 'assistant',
sessionId,
timestamp: at,
message: {
role: 'assistant',
model: 'claude-fable-5',
content: [
{ type: 'text', text: answer },
{ type: 'tool_use', name: 'Bash', input: { command: 'rg needle' } }
]
}
}),
JSON.stringify({
type: 'user',
sessionId,
timestamp: at,
message: {
role: 'user',
content: [{ type: 'tool_result', tool_use_id: 'toolu_1', content: output }]
}
})
)
}
const path = join(root, `${sessionId}.jsonl`)
const body = `${lines.join('\n')}\n`
await writeFile(path, body)
transcriptBytes += Buffer.byteLength(body)
files.push(path)
}
return { root, files, transcriptBytes, toolBytes, proseBytes }
}
@@ -0,0 +1,182 @@
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 <ref>:src/main/ai-vault/session-scanner-accumulator.ts | node config/scripts/session-timeline-benchmark.mjs
const target = resolve('src/main/ai-vault/session-scanner-accumulator.ts')
async function load(source) {
const result = await build({
stdin: {
contents: `
export {createAccumulator, cloneSessionAccumulator, updateTimeline, finalizeSession}
from './session-scanner-accumulator';
export {createClaudeSessionParseState, consumeClaudeSessionLine}
from './session-scanner-primary-parsers';`,
loader: 'ts',
resolveDir: dirname(target)
},
bundle: true,
write: false,
platform: 'node',
format: 'esm',
plugins: [
{
name: 'timeline-baseline',
setup(plugin) {
plugin.onLoad({ filter: /session-scanner-accumulator\.ts$/ }, () => ({
contents: source,
loader: 'ts',
resolveDir: dirname(target)
}))
}
}
]
})
return import(
`data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}`
)
}
const implementations = {
before: await load(readFileSync(0, 'utf8')),
after: await load(readFileSync(target, 'utf8'))
}
const file = { path: 'timeline.jsonl', mtimeMs: 0, modifiedAt: '2026-01-01T00:00:00.000Z' }
const create = (implementation) =>
implementation.createAccumulator({ agent: 'claude', sessionId: 'timeline', file })
const observable = ({ createdAt, updatedAt, latestTimestampMs }) => ({
createdAt,
updatedAt,
latestTimestampMs
})
let seed = 42
const random = (max) => {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return Math.floor((seed / 2 ** 32) * max)
}
const tokens = [
null,
undefined,
'',
'bad',
0,
-1,
Infinity,
Number.NaN,
8_640_000_000_000_001,
'1969-12-31T23:59:59.999Z',
'-000001-01-01T00:00:00.000Z',
'+010000-01-01T00:00:00.000Z',
'2026-01-01T01:00:00+01:00',
1_700_000_000.0009,
1_700_000_000_000.9,
1_700_000_000_000.1,
1_700_000_000_000 - 0.1
]
let differentialUpdates = 0
for (let trial = 0; trial < 3000; trial += 1) {
let before = create(implementations.before)
let after = create(implementations.after)
for (let index = 0; index < 32; index += 1) {
const input = random(2) ? tokens[random(tokens.length)] : 1_700_000_000_000 + random(1000) / 10
const update = (implementation, state) => {
try {
implementation.updateTimeline(state, input)
} catch (error) {
return String(error)
}
return null
}
assert.equal(update(implementations.after, after), update(implementations.before, before))
assert.deepEqual(observable(after), observable(before))
if (index === 15) {
before = implementations.before.cloneSessionAccumulator(before)
after = implementations.after.cloneSessionAccumulator(after)
}
differentialUpdates += 1
}
assert.deepEqual(
implementations.after.finalizeSession(after, 'linux'),
implementations.before.finalizeSession(before, 'linux')
)
}
const results = []
for (const records of [100, 10_000, 100_000]) {
for (const workload of [
'numeric-timeline',
'iso-timeline',
'out-of-order-timeline',
'claude-record-fold'
]) {
const timestamps = Array.from({ length: records }, (_, index) => {
const ms =
1_700_000_000_000 + (workload === 'out-of-order-timeline' ? random(records) : index)
return workload === 'numeric-timeline' ? ms : new Date(ms).toISOString()
})
const lines =
workload === 'claude-record-fold'
? timestamps.map((timestamp, index) =>
JSON.stringify({
type: index % 2 ? 'assistant' : 'user',
sessionId: 'timeline',
timestamp,
message: {
role: index % 2 ? 'assistant' : 'user',
content: 'Example transcript message'
}
})
)
: []
function run(arm) {
const implementation = implementations[arm]
const parser = implementation.createClaudeSessionParseState(file)
const state = workload === 'claude-record-fold' ? parser.accumulator : create(implementation)
const started = performance.now()
if (workload === 'claude-record-fold') {
for (const line of lines) {
implementation.consumeClaudeSessionLine(parser, line)
}
} else {
for (const timestamp of timestamps) {
implementation.updateTimeline(state, timestamp)
}
}
const ms = performance.now() - started
return {
ms,
result: implementation.finalizeSession(state, 'linux'),
timeline: observable(state)
}
}
const expected = run('before')
assert.deepEqual(run('after').result, expected.result)
/** @type {{ before: number[], after: number[] }} */
const samples = { before: [], after: [] }
for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) {
for (const arm of pair) {
const actual = run(arm)
samples[arm].push(actual.ms)
assert.deepEqual(actual.result, expected.result)
assert.deepEqual(actual.timeline, expected.timeline)
}
}
results.push({
records,
workload,
before: summarizeBenchmarkSamples(samples.before),
after: summarizeBenchmarkSamples(samples.after)
})
}
}
console.log(
JSON.stringify(
{ node: process.version, platform: process.platform, differentialUpdates, results },
null,
2
)
)
@@ -0,0 +1,149 @@
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'
const path = 'src/renderer/src/components/skills/skill-source-inventory.ts'
const arms = {}
for (const [name, source] of [
['baseline', readFileSync(0, 'utf8')],
['indexed', readFileSync(path, 'utf8')]
]) {
const { code } = await transform(source, { loader: 'ts', format: 'esm' })
const loaded = await import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`)
assert.equal(typeof loaded.summarizeSkillSources, 'function', 'Pipe the baseline module on stdin')
arms[name] = loaded.summarizeSkillSources
}
function verify(result) {
const expected = arms.baseline(result)
const actual = arms.indexed(result)
assert.deepEqual(actual, expected)
actual.forEach((entry, index) => assert.equal(entry.source, result.sources[index]))
return expected
}
let seed = 20260911
function random(max) {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return Math.floor((seed / 0x100000000) * max)
}
const paths = [
'/home/ada/.agents/skills',
'/repo/.agents/skills',
'/REPO/.agents/skills',
'/remote/folder/.claude/skills',
'C:\\Users\\Ada\\.codex\\skills',
'\\\\wsl$\\Ubuntu\\home\\ada\\.agents\\skills',
'',
'/not-listed'
]
verify(null)
for (let trial = 0; trial < 5000; trial++) {
const sources = Array.from({ length: random(25) }, (_, index) =>
Object.freeze({
id: `${index}`,
path: paths[random(paths.length - 1)],
exists: Boolean(random(2)),
skippedReason: [undefined, 'missing', 'remote-repo', 'unavailable'][random(4)]
})
)
const skills = []
const count = random(100)
for (let index = 0; index < count; index++) {
skills.push(
skills.length && random(4) === 0
? skills[random(skills.length)]
: Object.freeze({
rootPath: paths[random(paths.length)],
rootPaths: random(3)
? Object.freeze(Array.from({ length: random(15) }, () => paths[random(paths.length)]))
: undefined
})
)
}
verify(Object.freeze({ sources: Object.freeze(sources), skills: Object.freeze(skills) }))
}
console.log(
JSON.stringify({
differentialCases: 5001,
node: process.version,
platform: process.platform,
arch: process.arch
})
)
function workload(sourceCount, skillCount, rootsPerSkill) {
const paths = Array.from(
{ length: sourceCount || 1 },
(_, index) => `/repo-${index}/.agents/skills`
)
return {
sources: paths.slice(0, sourceCount).map((path, index) => ({
id: `${index}`,
path,
exists: index % 3 !== 0,
skippedReason: index % 5 ? 'missing' : 'unavailable'
})),
skills: Array.from({ length: skillCount }, (_, index) => ({
rootPath: paths[index % paths.length],
rootPaths: Array.from(
{ length: rootsPerSkill },
(_, rootIndex) => paths[(index + rootIndex) % paths.length]
)
}))
}
}
function median(values) {
const sorted = [...values].sort((a, b) => a - b)
return (sorted[3] + sorted[4]) / 2
}
for (const [sourceCount, skillCount, rootsPerSkill] of [
[0, 1000, 1],
[1, 1000, 0],
[1, 1000, 1],
[17, 0, 0],
[17, 20, 1],
[17, 200, 1],
[24, 1000, 3],
[87, 1000, 3],
[367, 5000, 3],
[17, 200, 17]
]) {
const input = workload(sourceCount, skillCount, rootsPerSkill)
const expected = verify(input)
const samples = { baseline: [], indexed: [] }
const repeats = Math.max(
5,
Math.floor(200000 / (Math.max(1, sourceCount) * Math.max(1, skillCount)))
)
for (const run of Object.values(arms)) {
for (let warmup = 0; warmup < Math.min(100, repeats); warmup++) {
run(input)
}
}
for (const pair of buildCounterbalancedSchedule(8, 'baseline', 'indexed')) {
for (const arm of pair) {
const start = performance.now()
let result
for (let repeat = 0; repeat < repeats; repeat++) {
result = arms[arm](input)
}
samples[arm].push((performance.now() - start) / repeats)
assert.deepEqual(result, expected)
}
}
console.log(
JSON.stringify({
sourceCount,
skillCount,
rootsPerSkill,
medianMs: Object.fromEntries(
Object.entries(samples).map(([arm, values]) => [arm, median(values)])
)
})
)
}
@@ -0,0 +1,331 @@
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 ssh-target-cleanup.ts source on stdin; no app or network is used.
const entry = path.resolve('src/renderer/src/store/slices/ssh-target-cleanup.ts')
const sources = [readFileSync(0, 'utf8'), readFileSync(entry, 'utf8')]
assert(
sources.every((source) => source.includes('export function buildRemovedSshTargetCleanupPatch'))
)
async function load(source, instrument = false) {
if (instrument) {
const spread = '...nextTabsByWorktree'
assert.equal(source.split(spread).length, 2)
source = source.replace(spread, '...countTabMapCopy(nextTabsByWorktree)')
source += `
export const tabMapCopies = { count: 0, entries: 0 };
function countTabMapCopy(map) {
tabMapCopies.count++;
tabMapCopies.entries += Reflect.ownKeys(map).length;
return map;
}
`
}
source += "\nexport { toAppSshPtyId } from '../../../../shared/ssh-pty-id';"
const result = await build({
entryPoints: [entry],
bundle: true,
platform: 'node',
format: 'esm',
write: false,
plugins: [
{
name: 'cleanup-source',
setup(builder) {
builder.onLoad({ filter: /ssh-target-cleanup\.ts$/ }, () => ({
contents: source,
loader: 'ts',
resolveDir: path.dirname(entry)
}))
}
}
]
})
const bundled = `${result.outputFiles[0].text}\n//# sourceURL=ssh-cleanup-benchmark-bundle.js`
return import(`data:text/javascript;base64,${Buffer.from(bundled).toString('base64')}`)
}
const modules = await Promise.all(sources.map((source) => load(source)))
const arms = modules.map((module) => module.buildRemovedSshTargetCleanupPatch)
const { toAppSshPtyId } = modules[0]
function emptyState() {
return {
repos: [],
worktreesByRepo: {},
detectedWorktreesByRepo: {},
restoredRuntimeHostIdByWorkspaceSessionKey: {},
tabsByWorktree: {},
ptyIdsByTabId: {},
lastKnownRelayPtyIdByTabId: {},
pendingCodexPaneRestartIds: {},
codexRestartNoticeByPtyId: {},
deferredSshSessionIdsByTabId: {},
pendingReconnectPtyIdByTabId: {},
directSshPaneRetryByTabId: {},
directSshLivePtyBindingByTabId: {},
directSshPaneRetryHistoryByTabId: {},
deferredSshReconnectTargets: [],
transientClearedAgentStatusConnectionIds: {},
sshConnectionStates: new Map(),
sshTargetLabels: new Map(),
sshTargetGenerations: new Map(),
remoteWorkspaceHydratedTargetIds: new Set(),
remoteWorkspaceSyncStatusByTargetId: {},
portForwardsByConnection: {},
detectedPortsByConnection: {},
sshCredentialQueue: []
}
}
function freezeState(value) {
if (!value || typeof value !== 'object' || Object.isFrozen(value)) {
return value
}
if (value instanceof Map || value instanceof Set) {
for (const item of value.values()) {
freezeState(item)
}
} else {
for (const item of Object.values(value)) {
freezeState(item)
}
}
return Object.freeze(value)
}
function tab(id, worktreeId, ptyId) {
return {
id,
worktreeId,
ptyId,
title: 'Terminal',
customTitle: null,
color: null,
sortOrder: 0,
createdAt: 0,
pendingActivationSpawn: true
}
}
function timedState(count, stride, catalog, tabsPerWorkspace = 1) {
const state = emptyState()
if (catalog) {
state.repos.push({ id: 'repo', path: '/remote', connectionId: 'removed' })
}
state.worktreesByRepo.repo = []
for (let i = 0; i < count; i++) {
const selected = stride > 0 && i % stride === 0
const worktreeId = catalog ? `repo::/remote/${i}` : `folder:${i}`
const ptyId = toAppSshPtyId(selected ? 'removed' : 'other', `pty-${i}`)
state.tabsByWorktree[worktreeId] = Array.from({ length: tabsPerWorkspace }, (_, j) =>
tab(`tab-${i}-${j}`, worktreeId, ptyId)
)
if (catalog) {
state.worktreesByRepo.repo.push({ id: worktreeId, repoId: 'repo', path: `/remote/${i}` })
}
}
return freezeState(state)
}
function compare(state, targetId) {
const before = structuredClone(state)
const results = arms.map((arm) => arm(state, targetId))
assert.deepEqual(results[1], results[0])
assert.deepEqual(state, before)
for (const key of Object.keys(results[0] ?? {})) {
assert.equal(results[1][key] === state[key], results[0][key] === state[key])
}
for (const [key, tabs] of Object.entries(state.tabsByWorktree)) {
const next = results.map((result) => result?.tabsByWorktree?.[key] ?? tabs)
assert.equal(next[1] === tabs, next[0] === tabs)
for (let i = 0; i < tabs.length; i++) {
assert.equal(next[1][i] === tabs[i], next[0][i] === tabs[i])
}
}
return results
}
let seed = 0x15c0ffee
function random(max) {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return (seed >>> 8) % max
}
for (let iteration = 0; iteration < 3000; iteration++) {
const state = emptyState()
const targets = ['removed', 'other', 'space / @ Unicode 🐳']
for (const targetId of targets) {
state.repos.push({ id: 'collision', path: `/remote/${targetId}`, connectionId: targetId })
if (random(2)) {
state.sshConnectionStates.set(targetId, { targetId, status: 'disconnected' })
}
if (random(2)) {
state.sshTargetLabels.set(targetId, targetId)
}
if (random(2)) {
state.sshTargetGenerations.set(targetId, random(10))
}
if (random(2)) {
state.remoteWorkspaceHydratedTargetIds.add(targetId)
}
if (random(2)) {
state.deferredSshReconnectTargets.push(targetId)
}
if (random(2)) {
state.transientClearedAgentStatusConnectionIds[targetId] = true
}
if (random(2)) {
state.remoteWorkspaceSyncStatusByTargetId[targetId] = { phase: 'synced' }
}
if (random(2)) {
state.portForwardsByConnection[targetId] = [{ localPort: 8000 }]
}
if (random(2)) {
state.detectedPortsByConnection[targetId] = [{ port: 8001 }]
}
if (random(2)) {
state.sshCredentialQueue.push({ targetId, requestId: targetId, kind: 'password' })
}
}
const count = random(24)
for (let i = 0; i < count; i++) {
const key = ['__proto__', 'constructor', 'toString'][i] ?? `folder:${i}`
const owner = targets[random(targets.length)]
const rows = Array.from({ length: random(5) }, (_, j) => {
const id = `tab-${i}-${j}`
const ptyId = [null, '', 'local-pty', 'ssh:bad', toAppSshPtyId(owner, `pty-${j}`)][random(5)]
if (random(3) === 0) {
state.ptyIdsByTabId[id] = [toAppSshPtyId(owner, 'split'), 'local-split']
}
if (random(3) === 0) {
state.lastKnownRelayPtyIdByTabId[id] = toAppSshPtyId(owner, 'last')
}
if (random(2)) {
state.deferredSshSessionIdsByTabId[id] = ptyId ?? 'local'
}
if (random(2)) {
state.pendingReconnectPtyIdByTabId[id] = toAppSshPtyId(owner, 'reconnect')
}
if (ptyId) {
state.pendingCodexPaneRestartIds[ptyId] = true
state.codexRestartNoticeByPtyId[ptyId] = {
previousAccountLabel: 'old',
nextAccountLabel: 'new'
}
}
const authority = {
targetId: targets[random(3)],
providerEpoch: 'epoch',
connectionGeneration: 1
}
if (random(2)) {
state.directSshPaneRetryByTabId[id] = { authority, attemptId: id, tabGeneration: 1 }
}
if (random(2)) {
state.directSshLivePtyBindingByTabId[id] = { authority, ptyId, tabGeneration: 1 }
}
if (random(2)) {
state.directSshPaneRetryHistoryByTabId[id] = { authority, attemptedAt: [10] }
}
return tab(id, key, ptyId)
})
Object.defineProperty(state.tabsByWorktree, key, { value: rows, enumerable: true })
if (i >= 3 && random(2)) {
const worktree = {
id: key,
repoId: 'collision',
path: `/remote/${i}`,
hostId: `ssh:${encodeURIComponent(owner)}`
}
;(state.worktreesByRepo.collision ??= []).push(worktree)
if (random(2)) {
state.detectedWorktreesByRepo.collision = { worktrees: [worktree] }
}
}
}
compare(freezeState(state), targets[random(3)])
}
console.log('3,000 frozen-state full-patch / identity differential cases passed')
// Instrument only the copy site for counts; timing arms above remain uninstrumented.
const counted = await Promise.all(sources.map((source) => load(source, true)))
for (const stride of [1, 10, 0]) {
const state = timedState(100, stride, false)
const expectedCopies = stride === 0 ? [0, 0] : [100 / stride, 1]
counted.forEach((module, index) => {
module.tabMapCopies.count = 0
module.tabMapCopies.entries = 0
assert.deepEqual(
module.buildRemovedSshTargetCleanupPatch(state, 'removed'),
arms[index](state, 'removed')
)
assert.deepEqual(module.tabMapCopies, {
count: expectedCopies[index],
entries: expectedCopies[index] * 100
})
})
console.log(
JSON.stringify({ stride, copiedEntries: counted.map((module) => module.tabMapCopies.entries) })
)
}
function sample(arm, state, repeats) {
const start = performance.now()
let changed = 0
for (let i = 0; i < repeats; i++) {
changed += arm(state, 'removed') !== null ? 1 : 0
}
assert(changed === 0 || changed === repeats)
return (performance.now() - start) / repeats
}
console.log(
JSON.stringify({
node: process.version,
platform: process.platform,
arch: process.arch,
unit: 'ms',
pairs: 8
})
)
for (const [count, stride, catalog, tabsPerWorkspace] of [
[1, 1, false, 1],
[10, 1, false, 1],
[100, 1, false, 1],
[500, 1, false, 1],
[1000, 1, false, 1],
[100, 10, false, 1],
[1000, 10, false, 1],
[1000, 0, false, 1],
[100, 1, true, 4],
[500, 1, true, 4]
]) {
const state = timedState(count, stride, catalog, tabsPerWorkspace)
compare(state, 'removed')
for (const arm of arms) {
const until = performance.now() + 80
while (performance.now() < until) {
sample(arm, state, 1)
}
}
const repeats = Math.max(1, Math.min(20000, Math.ceil(40 / sample(arms[0], state, 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], state, repeats))
}
}
const median = samples.map((values) => {
values.sort((a, b) => a - b)
return (values[3] + values[4]) / 2
})
console.log(
JSON.stringify({ count, stride, catalog, tabsPerWorkspace, repeats, median, samples })
)
}
@@ -0,0 +1,262 @@
#!/usr/bin/env node
// git show <base>:src/main/ipc/ssh-pty-source-obligation-state.ts | node config/scripts/ssh-source-frontier-benchmark.mjs
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'
import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs'
const statePath = 'src/main/ipc/ssh-pty-source-obligation-state.ts'
const baselineSource = readFileSync(0, 'utf8')
assert(baselineSource.includes('export function advanceSourceTerminalEnd'))
async function loadLedger(source) {
const result = await build({
entryPoints: ['src/main/ipc/ssh-pty-source-obligation-ledger.ts'],
bundle: true,
platform: 'node',
format: 'esm',
write: false,
plugins: [
{
name: 'source-obligation-state',
setup(builder) {
builder.onLoad({ filter: /ssh-pty-source-obligation-state\.ts$/ }, (args) => ({
contents: source,
loader: 'ts',
resolveDir: path.dirname(args.path)
}))
}
}
]
})
const encoded = Buffer.from(result.outputFiles[0].text).toString('base64')
return (await import(`data:text/javascript;base64,${encoded}`)).SshPtySourceObligationLedger
}
const [Baseline, Current] = await Promise.all([
loadLedger(baselineSource),
loadLedger(readFileSync(statePath, 'utf8'))
])
function identity(index = 0) {
return Object.freeze({
id: `pty-${index}`,
providerGeneration: index + 1,
clientGeneration: 2,
ownerGeneration: 3,
ptyIncarnation: `incarnation-${index}`,
deliveryToken: `token-${index}`
})
}
function sourceSpan(owner, id, start, length) {
return Object.freeze({
...owner,
spanId: id,
sourceStartSu: start,
sourceEndSu: start + length,
displayStart: start,
displayEnd: start + length,
data: 'x'.repeat(length),
splittable: true,
transform: Object.freeze({ transformed: false, rawLengthSu: length, scalarSafe: true })
})
}
function trace(Ledger, seed) {
let randomState = seed
const random = (bound) => {
randomState = (Math.imul(randomState, 1664525) + 1013904223) >>> 0
return Math.floor((randomState / 2 ** 32) * bound)
}
const closed = []
const ledger = new Ledger((owner) => closed.push(owner))
const owners = [identity(), identity(1)]
const starts = [seed % 3 === 0 ? Number.MAX_SAFE_INTEGER - 10_000 : 0, 100]
const reservations = []
const publications = []
const spans = []
const consumers = ['model', 'desktop', 'remote:viewer']
owners.forEach((owner, index) => ledger.open(owner, starts[index]))
const results = []
const rememberAck = (publication) => {
if (publication) {
publications.push(publication)
}
return publication?.ack ?? null
}
for (let step = 0; step < 120; step += 1) {
const owner = owners[random(owners.length)]
const selectedSpan = spans[random(spans.length)]
const spanId = selectedSpan?.spanId ?? 'missing'
const consumer = consumers[random(consumers.length)]
const operation = step < 20 ? random(2) : random(14)
try {
let value
switch (operation) {
case 0:
case 1: {
const start = ledger.snapshot(owner).receivedEndSu
const span = sourceSpan(owner, `span-${step}`, start, random(5))
spans.push(span)
const reservation = ledger.reserve(owner, span, consumers)
reservations.push(reservation)
value = random(8) === 0 ? ledger.rollback(reservation) : ledger.commit(reservation)
break
}
case 2:
case 3:
value = ledger.settle(spanId, consumer, 'accepted')
break
case 4:
value = ledger.beginTransfer(spanId, consumer, 'model', 'hidden')
break
case 5:
value = ledger.commitTransfer(spanId, consumer)
break
case 6:
value = ledger.cancelTransfer(spanId, consumer, 'canceled')
break
case 7:
value = ledger.rollbackTransfer(spanId, consumer)
break
case 8:
value = rememberAck(ledger.queueAck(owner))
break
case 9:
value = rememberAck(ledger.retryQueuedAck(owner))
break
case 10: {
const publication = publications[random(publications.length)]
const result = random(4) ? { ok: true } : { ok: false, error: new Error('write failed') }
value = publication?.onSettled(result)
break
}
case 11: {
const reservation = reservations[random(reservations.length)]
value = reservation ? ledger.rollbackCommitted(reservation) : false
break
}
case 12:
value = ledger.modelAcceptedEnd(owner)
break
case 13:
value = step < 100 ? ledger.spanIdentity(spanId) : ledger.seal(owner)
break
}
results.push({ operation, value })
} catch (error) {
results.push({ operation, error: [error.name, error.message] })
}
results.push(owners.map((entry) => ledger.snapshot(entry)))
}
for (const span of spans) {
if (ledger.hasRetainedSpan(span.spanId)) {
results.push(consumers.map((consumer) => ledger.obligation(span.spanId, consumer)))
for (const consumer of consumers) {
const obligation = ledger.obligation(span.spanId, consumer)
if (obligation.state === 'open') {
ledger.settle(span.spanId, consumer, 'drained')
} else if (obligation.state === 'transferring') {
ledger.commitTransfer(span.spanId, consumer)
}
}
results.push(owners.map((owner) => ledger.snapshot(owner)))
rememberAck(ledger.queueAck(span))
if (random(3) === 0) {
publications[random(publications.length)]?.onSettled({ ok: true })
}
} else {
results.push(null)
}
}
results.push(ledger.closeGeneration(1, 'connection closed'))
for (const publication of publications) {
publication.onSettled({ ok: true })
}
results.push(
ledger.closeAll('disposed'),
closed,
owners.map((owner) => ledger.snapshot(owner))
)
return results
}
for (let seed = 1; seed <= 2_000; seed += 1) {
assert.deepEqual(trace(Current, seed), trace(Baseline, seed), `seed ${seed}`)
}
console.log('2,000 differential traces / 240,000 commands passed (plus snapshots and cleanup).')
function runBatch(Ledger, spans, mode) {
const ledger = new Ledger()
const owner = spans[0]
ledger.open(owner)
const settle = (span) => {
ledger.settle(span.spanId, 'model', 'accepted')
ledger.settle(span.spanId, 'desktop', 'parsed')
}
for (const span of spans) {
ledger.commit(ledger.reserve(owner, span, ['model', 'desktop']))
if (mode !== 'backlog') {
settle(span)
if (mode === 'immediate') {
ledger.queueAck(owner)?.onSettled({ ok: true })
}
}
}
if (mode === 'backlog') {
for (const span of spans) {
settle(span)
}
}
ledger.queueAck(owner)?.onSettled({ ok: true })
return ledger.snapshot(owner)
}
function median(values) {
const ordered = values.toSorted((left, right) => left - right)
return (ordered[ordered.length / 2 - 1] + ordered[ordered.length / 2]) / 2
}
console.log(
JSON.stringify({ node: process.version, platform: process.platform, arch: process.arch })
)
const rows = []
for (const mode of ['immediate', 'delayed', 'backlog']) {
for (const count of [1, 16, 64, 256, 1_024]) {
const spans = Array.from({ length: count }, (_, index) =>
sourceSpan(identity(), `span-${index}`, index * 128, 128)
)
const expected = runBatch(Baseline, spans, mode)
assert.deepEqual(runBatch(Current, spans, mode), expected)
const iterations = Math.max(20, Math.floor(8_192 / count))
const measure = (Ledger) => {
let result
const start = performance.now()
for (let index = 0; index < iterations; index += 1) {
result = runBatch(Ledger, spans, mode)
}
const elapsed = (performance.now() - start) / iterations
assert.deepEqual(result, expected)
return elapsed
}
measure(Baseline)
measure(Current)
const samples = { baseline: [], current: [] }
for (const pair of buildCounterbalancedSchedule(8, 'baseline', 'current')) {
for (const arm of pair) {
samples[arm].push(measure(arm === 'baseline' ? Baseline : Current))
}
}
rows.push({
mode,
spans: count,
beforeMs: median(samples.baseline),
afterMs: median(samples.current)
})
}
}
console.table(rows)
console.log('Synthetic ledger CPU only; no network, renderer, or end-to-end latency measurement.')
@@ -0,0 +1,271 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { performance } from 'node:perf_hooks'
import ts from 'typescript-api'
import { build } from 'esbuild'
// Pipe the baseline capture module on stdin. No Electron or visible terminal is launched.
const renderer = path.resolve('src/renderer/src')
const entry = path.join(renderer, 'components/terminal-pane/terminal-shutdown-layout-capture.ts')
const sources = [readFileSync(0, 'utf8'), readFileSync(entry, 'utf8')]
assert(sources.every((source) => source.includes('export function captureTerminalShutdownLayout')))
const layoutFile = path.join(renderer, 'components/terminal-pane/layout-serialization.ts')
const layoutSource = readFileSync(layoutFile, 'utf8')
const layoutAst = ts.createSourceFile(layoutFile, layoutSource, ts.ScriptTarget.Latest, true)
const layoutNames = ['getLayoutChildNodes', 'serializePaneTree', 'serializeTerminalLayout']
const layoutFunctions = layoutAst.statements.filter(
(node) => ts.isFunctionDeclaration(node) && layoutNames.includes(node.name?.text)
)
assert.equal(layoutFunctions.length, layoutNames.length)
const layoutSubset = `import { isTerminalLeafId } from '../../../../shared/stable-pane-id';
${layoutFunctions.map((node) => node.getText(layoutAst)).join('\n')}`
async function load(source) {
const result = await build({
entryPoints: [entry],
bundle: true,
platform: 'node',
format: 'esm',
write: false,
alias: { '@': renderer },
plugins: [
{
name: 'capture-benchmark',
setup(builder) {
builder.onLoad({ filter: /terminal-shutdown-layout-capture\.ts$/ }, () => ({
contents: `${source}\nexport { fitsSessionScrollbackByteLimit, MAX_BUFFER_BYTES };`,
loader: 'ts',
resolveDir: path.dirname(entry)
}))
// Select unchanged layout declarations to exclude unrelated renderer module side effects.
builder.onLoad({ filter: /[/\\]layout-serialization\.ts$/ }, () => ({
contents: layoutSubset,
loader: 'ts',
resolveDir: path.dirname(layoutFile)
}))
builder.onResolve({ filter: /pane-terminal-output-scheduler$/ }, () => ({
path: 'capture-scheduler',
namespace: 'fixture'
}))
builder.onLoad({ filter: /.*/, namespace: 'fixture' }, () => ({
contents: 'export function flushTerminalOutput(terminal) { terminal.recordFlush?.(); }',
loader: 'js'
}))
}
}
]
})
const bundled = `${result.outputFiles[0].text}\n//# sourceURL=shutdown-byte-limit-benchmark-bundle.js`
return import(`data:text/javascript;base64,${Buffer.from(bundled).toString('base64')}`)
}
const modules = await Promise.all(sources.map(load))
const captures = modules.map((module) => module.captureTerminalShutdownLayout)
const predicates = modules.map((module) => module.fitsSessionScrollbackByteLimit)
class FixtureElement {
constructor({ classes = [], dataset = {}, children = [], firstElementChild = null } = {}) {
this.classList = { contains: (name) => classes.includes(name) }
this.dataset = dataset
this.children = children
this.firstElementChild = firstElementChild
this.style = { flex: '1' }
}
}
globalThis.HTMLElement = FixtureElement
function leafId(index) {
return `${String(index + 1).padStart(8, '0')}-1111-4111-8111-111111111111`
}
function captureArgs(panes, captureBuffers = true, cleared = false) {
const leaves = panes.map(
(pane) =>
new FixtureElement({
classes: ['pane'],
dataset: { leafId: pane.leafId }
})
)
let root = leaves[0] ?? null
for (const leaf of leaves.slice(1)) {
root = new FixtureElement({ classes: ['pane-split', 'is-horizontal'], children: [root, leaf] })
}
const prior = Object.fromEntries(panes.map((pane) => [pane.leafId, 'prior-buffer']))
return {
manager: { getPanes: () => panes, getActivePane: () => panes[0] ?? null },
container: new FixtureElement({ firstElementChild: root }),
expandedPaneId: panes[1]?.id ?? null,
paneTransports: new Map(
panes.map((pane, index) => [
pane.id,
{
getPtyId: () => (index % 2 ? null : `ssh:target@@pty-${index}`)
}
])
),
paneTitlesByPaneId: Object.fromEntries(panes.map((pane) => [pane.id, `title-${pane.id}`])),
existingLayout: Object.freeze({
root: null,
activeLeafId: null,
expandedLeafId: null,
buffersByLeafId: Object.freeze(prior),
scrollbackRefsByLeafId: Object.freeze({ [leafId(0)]: 'v1-prior' }),
ptyIdsByLeafId: Object.freeze({ [leafId(1)]: 'local-prior' })
}),
captureBuffers,
clearedScrollbackLeafIds: new Set(cleared ? [leafId(0)] : [])
}
}
function syntheticFixture(config) {
const events = []
const panes = config.units.map((unit, index) =>
Object.freeze({
id: index + 1,
leafId: leafId(index),
terminal: Object.freeze({
options: { scrollback: config.rows },
cols: 120,
rows: 40,
buffer: { active: { cursorX: index, cursorY: index } },
recordFlush: () => events.push(['flush', index])
}),
serializeAddon: {
serialize(options) {
const rows = options?.scrollback ?? 0
events.push(['serialize', index, rows])
if (config.fail && index === 0) {
throw new Error('fixture serializer failure')
}
return unit.repeat(Math.max(0, rows))
}
}
})
)
return { args: captureArgs(panes, config.capture, config.cleared), events }
}
let seed = 0x17c0ffee
function random(max) {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return (seed >>> 8) % max
}
const limit = modules[0].MAX_BUFFER_BYTES
assert.equal(modules[1].MAX_BUFFER_BYTES, limit)
const units = ['x', 'é', '界', '😀', '\ud83d', '\udc00', '\x1b[31mtext\x1b[0m\r\n', '']
let comparisons = 0
function compareFixture(config) {
const results = captures.map((capture) => {
const fixture = syntheticFixture(config)
const layout = capture(fixture.args)
return { layout, events: fixture.events }
})
assert.deepEqual(results[1], results[0])
comparisons++
}
for (const unit of units.filter(Boolean)) {
for (let delta = -2; delta <= 2; delta++) {
const rows = Math.floor((limit - 7) / Buffer.byteLength(unit)) + delta
compareFixture({ units: [unit], rows, capture: true, cleared: false, fail: false })
}
}
for (let iteration = 0; iteration < 1500; iteration++) {
compareFixture({
units: Array.from({ length: random(4) }, () => units[random(units.length)].repeat(random(50))),
rows: random(2000),
capture: random(5) !== 0,
cleared: random(3) === 0,
fail: random(9) === 0
})
}
for (const unit of units) {
for (const length of [0, 1, 64, limit - 1, limit, limit + 1]) {
const input = unit.repeat(length)
assert.equal(predicates[1](input), predicates[0](input))
}
}
console.log(`${comparisons} full capture layouts/event traces and 48 byte-fit cases match`)
function sample(arm, input, repeats) {
const start = performance.now()
let output
for (let i = 0; i < repeats; i++) {
output = arm(input)
}
return { elapsed: (performance.now() - start) / repeats, output }
}
function benchmark(name, input, arms, minimumRepeats = 1) {
const expected = arms[0](input)
assert.deepEqual(arms[1](input), expected)
for (const arm of arms) {
const until = performance.now() + 250
while (performance.now() < until) {
sample(arm, input, 1)
}
}
const repeats = Math.max(
minimumRepeats,
Math.min(100000, Math.ceil(50 / sample(arms[0], input, 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], input, repeats)
times[index].push(result.elapsed)
assert.deepEqual(result.output, expected)
}
}
const median = times.map((values) => {
values.sort((a, b) => a - b)
return (values[3] + values[4]) / 2
})
console.log(JSON.stringify({ name, repeats, median, times }))
}
console.log(
JSON.stringify({
node: process.version,
platform: process.platform,
arch: process.arch,
pairs: 8,
unit: 'ms'
})
)
for (const size of [64, 32768, limit, 2 * 1024 * 1024]) {
benchmark(`fit ASCII ${size} code units`, 'x'.repeat(size), predicates)
benchmark(`fit Unicode ${size} code units`, '界'.repeat(size), predicates)
}
const xterm = await import('@xterm/headless')
const addon = await import('@xterm/addon-serialize')
const Terminal = xterm.Terminal ?? xterm.default.Terminal
const SerializeAddon = addon.SerializeAddon ?? addon.default.SerializeAddon
for (const [rows, unicode, paneCount] of [
[50, false, 1],
[500, false, 1],
[5000, false, 1],
[5000, true, 1],
[500, false, 8]
]) {
const panes = []
for (let index = 0; index < paneCount; index++) {
const terminal = new Terminal({ cols: 120, rows: 40, scrollback: 5000, allowProposedApi: true })
const serializeAddon = new SerializeAddon()
terminal.loadAddon(serializeAddon)
const row = unicode ? '界é😀'.repeat(15) : 'line x'.repeat(18)
await new Promise((resolve) => terminal.write(`${row}\r\n`.repeat(rows), resolve))
panes.push({ id: index + 1, leafId: leafId(index), terminal, serializeAddon })
}
try {
benchmark(
`full capture ${paneCount} panes / ${rows} ${unicode ? 'Unicode' : 'ASCII'} lines`,
captureArgs(panes),
captures,
4
)
} finally {
panes.forEach((pane) => pane.terminal.dispose())
}
}
@@ -0,0 +1,114 @@
import fs from 'node:fs'
import { execFileSync } from 'node:child_process'
import { dirname, resolve } from 'node:path'
import assert from 'node:assert/strict'
import { build } from 'esbuild'
const file = 'src/shared/native-chat-tool-summary.ts'
const baseline = process.argv[2] ?? '20ab9950654'
const beforeSource = execFileSync('git', ['show', `${baseline}:${file}`], {
encoding: 'utf8',
windowsHide: true
})
const afterSource = fs.readFileSync(file, 'utf8')
async function load(contents) {
const { outputFiles } = 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(outputFiles[0].text).toString('base64')}`
)
}
const before = await load(beforeSource),
after = await load(afterSource)
const display = (m, input) => {
const d = m.createToolInputDisplay(input)
return { ...d, formatDetail: d.formatDetail() }
}
let seed = 8121
const random = (max) => {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return Math.floor((seed / 4294967296) * max)
}
const pieces = [
'a',
'b',
'…',
'😀',
'\ud800',
'\udc00',
'\0',
'\t',
'\r',
'\n',
' ',
'\v',
'\f',
'\u00a0',
'\u1680',
'\u2000',
'\u200a',
'\u2028',
'\u2029',
'\u202f',
'\u205f',
'\u3000',
'\ufeff',
'\u0085',
'\u200b'
]
for (let i = 0; i < 3000; i++) {
const input = Array.from({ length: random(500) }, () => pieces[random(pieces.length)]).join('')
assert.equal(after.summarizeToolInput(input), before.summarizeToolInput(input))
assert.deepEqual(display(after, input), display(before, input))
}
for (const input of [
`${'a'.repeat(79)}…`,
'a'.repeat(80) + ' '.repeat(500),
' '.repeat(100000),
`${'\n'.repeat(100000)}x`,
{ command: 'a '.repeat(50000) },
{ file_path: 'a '.repeat(500) },
{ x: 'a '.repeat(500) },
JSON.stringify({ command: 'a '.repeat(50000) })
]) {
assert.deepEqual(display(after, input), display(before, input))
}
for (const [shape, input] of [
['tiny', 'ls -la'],
['100KB', 'a b\n\t'.repeat(15000)],
['1MB', 'a b\n\t'.repeat(150000)],
['all-space', ' '.repeat(1000000)],
['leading', `${' '.repeat(1000000)}x`],
['trailing', `x${' '.repeat(1000000)}`],
['long-word', 'x'.repeat(1000000)]
]) {
const samples = { before: [], after: [] }
for (let i = 0; i < 20; i++) {
before.createToolInputDisplay(input)
after.createToolInputDisplay(input)
}
for (let r = 0; r < 8; r++) {
for (const [label, m] of r % 2
? [
['after', after],
['before', before]
]
: [
['before', before],
['after', after]
]) {
global.gc()
const start = process.cpuUsage()
for (let i = 0; i < 20; i++) {
m.createToolInputDisplay(input)
}
const cpu = process.cpuUsage(start)
samples[label].push((cpu.user + cpu.system) / 20000)
}
}
console.log(JSON.stringify({ shape, samples }))
}
@@ -0,0 +1,191 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { Readable } from 'node:stream'
import { performance } from 'node:perf_hooks'
import { build } from 'esbuild'
import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs'
const modulePath = 'src/main/native-chat/transcript-stream-lines.ts'
const baselineSource = readFileSync(0, 'utf8')
assert.ok(baselineSource.includes('decodeTranscriptStream'), 'Pipe the baseline module on stdin')
const arms = {}
for (const [name, source] of [
['baseline', baselineSource],
['fragmented', readFileSync(modulePath, 'utf8')]
]) {
const output = await build({
stdin: {
contents: `${source}\nexport { decodeClaudeTranscriptLine } from './transcript-line-decoders'`,
loader: 'ts',
resolveDir: dirname(resolve(modulePath))
},
bundle: true,
platform: 'node',
format: 'esm',
write: false
})
arms[name] = await import(
`data:text/javascript;base64,${Buffer.from(output.outputFiles[0].text).toString('base64')}`
)
}
const path = '/fixture/transcript.jsonl'
const decode = (line, id) => ({ id, line })
let seed = 90211
function random(max) {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
return Math.floor((seed / 0x100000000) * max)
}
async function observe(arm, chunks, start, includeTrailing, throwAt) {
const calls = []
const stream = Readable.from(chunks)
try {
const result = await arm.decodeTranscriptStream(
stream,
path,
start,
(line, id) => {
calls.push({ line, id })
if (calls.length === throwAt) {
throw new Error('fixture decode error')
}
return line.startsWith('skip') ? null : decode(line, id)
},
includeTrailing
)
return { calls, result, destroyed: stream.destroyed }
} catch (error) {
return { calls, error: error.message, destroyed: stream.destroyed }
}
}
const tokens = ['a', '\r', '\n', '😀', 'é中', '\ud83d', '\ude00', 'skip', '\x00', '\u2028']
for (let trial = 0; trial < 5000; trial++) {
let chunks
if (trial % 2) {
const raw = Buffer.from(Array.from({ length: random(100) }, () => random(256)))
chunks = []
for (let offset = 0; offset < raw.length;) {
const end = Math.min(raw.length, offset + 1 + random(8))
chunks.push(raw.subarray(offset, end))
offset = end
}
} else {
chunks = Array.from({ length: random(30) }, () => {
const text = Array.from({ length: random(15) }, () => tokens[random(tokens.length)]).join('')
return random(3) ? text : Buffer.from(text)
})
}
const start = [0, 123, -1, 0.5, Number.MAX_SAFE_INTEGER - 3, Infinity, Number.NaN][random(7)]
const includeTrailing = Boolean(random(2))
const throwAt = random(7) === 0 ? 1 + random(4) : Infinity
const expected = await observe(arms.baseline, chunks, start, includeTrailing, throwAt)
const actual = await observe(arms.fragmented, chunks, start, includeTrailing, throwAt)
assert.deepEqual(actual, expected)
}
console.log(
JSON.stringify({
differentialCases: 5000,
node: process.version,
platform: process.platform,
arch: process.arch
})
)
function splitInput(input, chunkSize, kind) {
const raw = kind === 'buffer' ? Buffer.from(input) : input
const chunks = []
for (let offset = 0; offset < raw.length; offset += chunkSize) {
chunks.push(raw.slice(offset, offset + chunkSize))
}
return chunks
}
const workloads = []
for (const [size, chunkSize, kind] of [
[4096, 65536, 'string'],
[2 * 1024 * 1024, 65536, 'string'],
[8 * 1024 * 1024, 65536, 'string'],
[2 * 1024 * 1024, 4096, 'buffer'],
[8 * 1024 * 1024, 1024 * 1024, 'buffer']
]) {
const input = `${'x'.repeat(size)}\n`
workloads.push({
name: `${size}B record/${chunkSize}B ${kind}`,
chunks: splitInput(input, chunkSize, kind),
includeTrailing: true
})
}
workloads.push({
name: '10000 short records/64KiB strings',
chunks: splitInput('ordinary record\r\n'.repeat(10000), 65536, 'string'),
includeTrailing: true
})
for (const includeTrailing of [false, true]) {
workloads.push({
name: `2MiB partial trailing/include=${includeTrailing}`,
chunks: splitInput(`complete\n${'x'.repeat(2 * 1024 * 1024)}`, 65536, 'string'),
includeTrailing
})
}
for (const size of [4096, 2 * 1024 * 1024, 8 * 1024 * 1024]) {
const input = `${JSON.stringify({
type: 'user',
uuid: 'message-1',
timestamp: '2026-09-11T10:00:00Z',
message: { role: 'user', content: 'x'.repeat(size) }
})}\n`
workloads.push({
name: `${size}B Claude record with real decoder`,
chunks: splitInput(input, 65536, 'string'),
includeTrailing: true,
claude: true
})
}
function median(values) {
const sorted = [...values].sort((a, b) => a - b)
return (sorted[3] + sorted[4]) / 2
}
for (const workload of workloads) {
const run = (arm) =>
arms[arm].decodeTranscriptStream(
Readable.from(workload.chunks),
path,
0,
workload.claude ? arms[arm].decodeClaudeTranscriptLine : decode,
workload.includeTrailing
)
const expected = await run('baseline')
assert.deepEqual(await run('fragmented'), expected)
if (workload.claude) {
assert.equal(expected.messages.length, 1)
}
const samples = { baseline: [], fragmented: [] }
const repeats = workload.chunks.length === 1 ? 100 : 5
for (const arm of Object.keys(arms)) {
for (let warmup = 0; warmup < 5; warmup++) {
await run(arm)
}
}
for (const pair of buildCounterbalancedSchedule(8, 'baseline', 'fragmented')) {
for (const arm of pair) {
const start = performance.now()
let actual
for (let repeat = 0; repeat < repeats; repeat++) {
actual = await run(arm)
}
samples[arm].push((performance.now() - start) / repeats)
assert.deepEqual(actual, expected)
}
}
console.log(
JSON.stringify({
name: workload.name,
medianMs: Object.fromEntries(
Object.entries(samples).map(([arm, values]) => [arm, median(values)])
)
})
)
}
@@ -20,8 +20,8 @@ const TILES = [
{
id: 'tile-01',
sourceRoot: ROOT,
gifRelativePath: 'docs/assets/feature-wall/parallel-worktrees.gif',
posterRelativePath: 'docs/assets/feature-wall/parallel-worktrees.jpg'
gifRelativePath: 'docs/site/public/docs/tab-split.gif',
posterRelativePath: 'docs/site/public/docs/posters/tab-split.jpg'
},
{
id: 'tile-02',
@@ -1,7 +1,10 @@
import { readFileSync } from 'node:fs'
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { pathToFileURL } from 'node:url'
import { describe, expect, it } from 'vitest'
import { parse } from 'yaml'
import { runProcessSync } from '../../src/shared/child-process/run-process'
const projectDir = resolve(import.meta.dirname, '../..')
@@ -15,13 +18,74 @@ const REF_MIRRORS = [
]
describe('ref-mirroring vet steps', () => {
it('keeps the full-history adhoc checkout on the same case-safe backend', () => {
it.each(['daily', 'hourly', 'adhoc'])('%s builds only need the current commit', (channel) => {
const job = readWorkflow(`.github/workflows/${channel}-mac-build.yml`).jobs[
`build-${channel}-mac`
]
const checkout = job.steps.find((step) => step.uses === 'actions/checkout@v6')
expect(checkout.with['fetch-depth']).toBe(1)
expect(job.steps.some((step) => step.run?.includes('gh release list'))).toBe(true)
expect(
job.steps.some((step) => step.run?.includes('ORCA_PUBLISHED_VERSIONS="$published"'))
).toBe(true)
})
it('retains release-cut history for version reservation and retry ancestry', () => {
const checkout = readWorkflow('.github/workflows/release-cut.yml').jobs.cut.steps.find(
(step) => step.uses === 'actions/checkout@v6'
)
expect(checkout.with['fetch-depth']).toBe(0)
})
it('resolves identical dev identities in full and depth-one checkouts without local tags', () => {
const directory = mkdtempSync(join(tmpdir(), 'orca-checkout-identity-'))
const source = join(directory, 'source')
const shallow = join(directory, 'shallow')
const run = (program, args, cwd) => {
const result = runProcessSync({ program, args, cwd })
expect(result.code, result.stderr).toBe(0)
return result.stdout.trim()
}
const git = (args, cwd = directory) => run('git', args, cwd)
try {
git(['init', source])
git(['config', 'user.name', 'CI test'], source)
git(['config', 'user.email', 'ci@example.invalid'], source)
writeFileSync(join(source, 'package.json'), JSON.stringify({ version: '1.4.165-rc.0' }))
git(['add', 'package.json'], source)
git(['-c', 'commit.gpgsign=false', 'commit', '-m', 'initial'], source)
git(['tag', 'v1.4.167'], source)
git(['-c', 'commit.gpgsign=false', 'commit', '--allow-empty', '-m', 'head'], source)
git(['clone', '--depth=1', '--no-tags', pathToFileURL(source).href, shallow])
expect(git(['rev-list', '--count', 'HEAD'], shallow)).toBe('1')
expect(git(['tag', '--list'], shallow)).toBe('')
const script = `
const result = [];
for (const [channel, exported] of [['daily', 'Daily'], ['hourly', 'Hourly'], ['adhoc', 'Adhoc']]) {
const module = await import(${JSON.stringify(pathToFileURL(join(projectDir, 'config/scripts/')).href)} + channel + '-build-version.mjs');
const date = new Date('2026-09-12T00:00:00Z');
result.push(channel === 'adhoc'
? module.getAdhocBuildIdentity(date, 'branch', ['v1.4.167'])
: module['get' + exported + 'BuildIdentity'](date, { publishedVersions: ['v1.4.167'], releaseNames: [] }));
}
process.stdout.write(JSON.stringify(result));
`
const identities = (cwd) => run(process.execPath, ['--input-type=module', '-e', script], cwd)
expect(identities(shallow)).toBe(identities(source))
expect(
JSON.parse(identities(shallow)).every((identity) => identity.version.startsWith('1.4.168-'))
).toBe(true)
} finally {
rmSync(directory, { recursive: true, force: true })
}
})
it('checks out only the vetted commit without remirroring refs', () => {
const steps = readWorkflow('.github/workflows/adhoc-mac-build.yml').jobs['build-adhoc-mac']
.steps
const checkout = steps.find((step) => step.name === 'Checkout the requested ref')
expect(checkout.env.GIT_DEFAULT_REF_FORMAT).toBe('reftable')
expect(checkout.with.ref).toBe('${{ steps.vetted.outputs.sha }}')
expect(checkout.with['fetch-depth']).toBe(0)
expect(checkout.with['fetch-depth']).toBe(1)
expect(checkout.with['persist-credentials']).toBe(false)
})
@@ -95,7 +95,7 @@ describe('release ref trust with case-twin names', () => {
expect(result.stdout).toContain('Refusing to build PR ref')
})
it('preserves both case variants in the subsequent full-history checkout', async () => {
it('checks out the vetted SHA shallowly without mirroring case-twin refs again', async () => {
const checkout = join(directory, 'checkout')
const env = { ...identity, ...macCheckout.env }
await git(['init', checkout], env)
@@ -105,21 +105,15 @@ describe('release ref trust with case-twin names', () => {
checkout,
'fetch',
'--no-tags',
`--depth=${macCheckout.with['fetch-depth']}`,
repository,
'+refs/heads/*:refs/remotes/origin/*',
'+refs/tags/*:refs/tags/*'
upper
],
env
)
await git(['-C', checkout, 'checkout', '--detach', upper], env)
for (const [ref, sha] of [
['refs/remotes/origin/Fix', upper],
['refs/remotes/origin/fix', lower],
['refs/tags/Release', upper],
['refs/tags/release', lower]
]) {
expect(await git(['-C', checkout, 'rev-parse', `${ref}^{commit}`], env)).toBe(sha)
}
expect(await git(['-C', checkout, 'rev-parse', 'HEAD'], env)).toBe(upper)
expect(await git(['-C', checkout, 'rev-list', '--count', 'HEAD'], env)).toBe('1')
expect(await git(['-C', checkout, 'for-each-ref', '--format=%(refname)'], env)).toBe('')
})
})
+4
View File
@@ -1,5 +1,6 @@
import { resolve } from 'node:path'
import { defineConfig } from 'vitest/config'
import TimingSequencer from './scripts/ci-unit-sequencer.mjs'
const windowsTestWorkerOptions = process.platform === 'win32' ? { maxWorkers: 4 } : {}
@@ -15,6 +16,9 @@ export default defineConfig({
},
test: {
environment: 'node',
...(process.env.ORCA_BALANCE_UNIT_SHARDS === '1'
? { sequence: { sequencer: TimingSequencer } }
: {}),
// Why: Node 26's undefined Web Storage globals prevent Vitest from installing happy-dom's.
// Why --expose-gc: retention tests need a deterministic collection point to measure what a queue really holds.
execArgv: ['--no-experimental-webstorage', '--expose-gc'],

Some files were not shown because too many files have changed in this diff Show More