E2E Automation
Desktop E2E fixtures use GPT 5.6 Luna. Real runtime catalogs advertise both GPT-5.6 Luna and GPT 5.6 Luna, so default-model selection assertions accept these two display names while retaining model ID and provider checks. Task restoration assertions retain the fixture's original display name.
Desktop checkpoint cleanup retains backend-rs-logs/ so CI can upload Rust gateway startup logs. Settings follows the current global route while retaining the original tab's application route, allowing Back to restore the application.
Wework provides a dedicated Playwright E2E entrypoint and a test-only frontend automation bridge for operating the Wework Vite/React frontend in CI. The default entrypoint runs in browser mode, which covers most frontend interactions; the desktop task-flow E2E below covers native-window and task-execution behavior.
Issue MCP attachment coverage uses the shared setup-minio-e2e Action on Linux and Windows to build and verify the same pinned real MinIO version. Only main writes caches; scenarios use the platform executable through WEWORK_E2E_MINIO_BIN.
The first model-routing task and real Harness CLI launch use the workbench startup budget. Streaming scroll waits for the model request before applying the original ten-second UI assertion. After changing projects, wait for the execution mode control to become available. Scroll moved board cards into view before checking result visibility, and use the responsive view helper for cloud Stand-in navigation.
Numeric Process Explorer filters match the exact PID, excluding matches in other processes' launch arguments or PID substrings. Text filters still search process names and launch details. The application-menu checkpoint retains its assertion that filtering by the application PID excludes unrelated helpers.
Running Tests
Install the Playwright browser before the first run:
pnpm --filter wework exec playwright install chromium
Run Wework E2E:
pnpm --filter wework e2e
Run the real desktop task-flow E2E:
pnpm --filter wework e2e:desktop
The stand-in-email checkpoint uses an isolated GreenMail IMAP/SMTP service and requires Java 11 or newer; CI uses Java 21. Ensure that java and keytool on PATH come from a supported JDK before running it.
Run only the Codex cloud-config recovery regression:
pnpm --filter wework e2e:desktop -- --segment codex-cloud-config-recovery
This checkpoint uses real Electron, executor, and Codex processes, with an isolated model subscription service simulating enterprise cloud-config bundle failures. It covers existing-thread continuation, new threads, configuration and model reads, Goal recovery after restart, official subscription authentication recovery, and concurrent authentication protection while a cloud-model turn runs. Assertions include the final reply, native transcripts, and task state after reload. The fixture creates its own account and directories. Existing desktop CI Core shards run this checkpoint.
Run only the cloud-project desktop E2E:
pnpm --filter wework e2e:desktop:cloud
Run only the plugin marketplace, install, chat-use, and uninstall flow:
pnpm --filter wework e2e:desktop:plugins
Run only the embedded-browser Agent operation regression:
pnpm --filter wework e2e:desktop:embedded-browser
Run the desktop memory regression on macOS, including streaming growth and the whole-process check with 10 concurrent tasks:
pnpm --filter wework e2e:desktop:memory
Run only the regression that keeps “Thinking” below already-visible streaming assistant text and removes it after completion:
pnpm --filter wework e2e:desktop:streaming-text
The command starts a test-only Vite server through wework/playwright.config.ts:
pnpm exec vite --host 127.0.0.1 --port 4174 --mode e2e
It also starts Responses API, Sites upstream, and Connector upstream mocks:
node e2e/utils/mock-response-api-server.mjs
node e2e/utils/mock-sites-upstream-server.mjs
node e2e/utils/mock-connector-upstream-server.mjs
Default configuration:
VITE_WEWORK_E2E=trueVITE_WEWORK_RUNTIME_MODE=backendVITE_LOGIN_MODE=passwordWEWORK_RESPONSE_API_MOCK_URL:http://127.0.0.1:9998WEWORK_SITES_UPSTREAM_MOCK_URL:http://127.0.0.1:9997WEWORK_CONNECTOR_UPSTREAM_MOCK_URL:http://127.0.0.1:9996
All three mock ports can be overridden with matching *_PORT environment variables: WEWORK_RESPONSE_API_MOCK_PORT, WEWORK_SITES_UPSTREAM_MOCK_PORT, and WEWORK_CONNECTOR_UPSTREAM_MOCK_PORT. If a port is overridden, tests can also receive the full matching URL environment variable directly.
Tests do not mock backend APIs. When Backend is not running, the login-page smoke test only verifies that the frontend renders the login entrypoint. Business flows after login must start a real Backend and required services in CI.
Desktop Task-Flow E2E
wework/e2e/desktop/task-flow.e2e.mjs covers the real task lifecycle in a local workspace and lets product distributions inject an optional desktop scenario:
- Builds and starts the real Electron Wework application, opening an isolated workspace with
--open-workspace. - Starts the real
wegent-executorsidecar, which starts a realcodex app-server. - Fills in a task and clicks send in the native WebView, then waits for the real conversation to render.
- Verifies the request issued by Codex to the model service, the workspace file written by a real Codex tool call, and the final UI response.
- Sends a follow-up in the same conversation and verifies its request and rendered response.
- Starts a streaming response, cancels it through the desktop UI, verifies the stopped task state and rendered stop notice, then verifies the composer accepts a subsequent message.
- Forces one model failure, clicks retry in the rendered error card, and verifies the retried request and final response.
- Creates a short two-turn conversation, switches to a new conversation, reopens the original conversation, verifies that both user messages, both assistant messages, and all four unified virtual rows remain mounted, and confirms that the first message stays near the top of the viewport instead of losing content or leaving a large virtual-list gap.
- Dynamically loads a product scenario when
WEWORK_E2E_DESKTOP_SCENARIO_MODULEis set. The public runner supplies only HTTP, WebSocket, control, and diagnostic lifecycles; it contains no concrete product protocol or assertions.
The test does not simulate Wework, Executor, or Codex. To keep regression results deterministic and avoid requiring a real provider account, it starts only a loopback model service implementing OpenAI Responses, OpenAI Chat Completions, and Anthropic Messages. Each interface runs a text response plus a send → apply_patch → tool result → completion lifecycle, while real Codex executes the tool in the isolated workspace.
Reply predictions and their image-description requests are dispatched independently of the foreground scenario. A previous conversation's background image request can arrive after the protocol matrix starts. The model fixture identifies it first, retains the image-description protocol checks and response, and leaves matrix state unchanged. desktop-reply-prediction-fixture.test.mjs covers this cross-scenario path; foreground endpoint and model ID assertions remain intact.
The model protocol matrix defines 18 combinations across execution location, model source, and protocol:
- Local execution covers local custom models, built-in Codex models, and cloud Model CRDs across all three protocols, for 9 complete text and tool lifecycles.
- Cloud execution also covers local custom models, built-in Codex models, and cloud Model CRDs across all three protocols, for 9 complete text and tool lifecycles.
- The first local-custom-model-to-cloud-device combination verifies the synchronization confirmation, target Executor catalog write, and cloud Codex restart. All three local-model protocols must then complete their text and
apply_patchtool lifecycles.
Matrix submissions use a 10-second timeout. If the composer already displays a submission error, the runner throws that exact error immediately. Otherwise, a stalled protocol stage reports its current stage and captured requests instead of waiting through the general UI timeout.
e2e:desktop:streaming-text runs an isolated streaming-message state regression through a scenario module. It uses the real Electron renderer, Executor, and Codex app-server while a loopback Responses SSE keeps a partial reply active. The scenario first streams one assistant item as final_answer and completes the same item as commentary. It verifies that Executor sends response.block.created.replacesItemId, allowing the frontend to atomically migrate the visible assistant text into a process block so the page contains one process copy and no final-answer copy. The scenario then verifies that streaming reasoning appears as “Thinking · summary” and that the reasoning summary and its placeholder are removed after completion. It starts a long-running command and confirms that the tool-row duration continues increasing after switching tasks while the tool-group header does not show a turn-wide aggregate duration. The scenario next builds a long multi-turn conversation beyond the virtualization threshold and verifies the “Thinking” position, user scroll anchor, streaming growth, and viewport stability after reopening the task. It uses scrollFromBottomAsUser to move a fixed distance upward from the bottom, then combines startScrollStabilitySampling and getScrollStabilitySample to record anchor geometry, DOM changes, and real scroll events while response chunks arrive. The gate requires no back-and-forth anchor movement and no programmatic scroll events after the user stops. It also verifies that the waiting state disappears after completion. The scenario retains screenshots for phase reclassification, reasoning, tool timing, ready, streaming, and completed stages; its scenario-specific Codex configuration disables plugin extensions to isolate direct message streaming.
e2e:desktop:embedded-browser runs the embedded-browser Agent operation regression through a scenario module. It uses the real Electron renderer, Executor, Codex app-server, and browser MCP server, opens a local fixture page, and verifies the Electron WebContentsView bridge control path. The scenario covers bridge identity lookup, authenticated bridge requests, page open, structured inspect, fill, click, wait, scroll, screenshot, capabilities, high-risk action approval, and combined MCP tools such as open_and_inspect and wait_and_inspect. It also starts a long waitFor and then verifies an independent click is not blocked, preventing bridge concurrency regressions. When local-file support changes, the scenario also opens a file:// HTML fixture, Markdown and extensionless text fixtures, and a local folder fixture through the bridge, and verifies that an unpreviewable local file shows a toast instead of entering the download list. Results are written to embedded-browser-agent-result.json.
The conversation-state checkpoint keeps the reading paragraph visible before and after opening a file panel. Shared viewport restoration incorporates reader movement into the same semantic anchor baseline, so scrolling away from the bottom cannot make a programmatic correction look like native range clamping and apply the same layout shift twice. useReaderViewportAnchor.test.ts reproduces the CI scroll coordinates and 48px height change across consecutive restorations; existing E2E geometry assertions remain unchanged.
The renderer-storage checkpoint writes model, draft, and layout localStorage
fixtures in the real packaged Electron app, flushes the persistence queue, and
restarts Core DSH. It requires the page origin after restart to differ from the
original origin before checking that all three values were restored. This makes
the regression exercise desktop persistence across origins instead of an
ordinary read within the same renderer process.
The main desktop flow's short-conversation layout regression stores short-conversation-00-ready.png, short-conversation-01-prompt-filled.png, short-conversation-02-completed-top-aligned.png, and short-conversation-layout-metrics.json. The final screenshot and metrics are captured after switching away and reopening the conversation. The gate requires the first message to remain within 160px of the message viewport's top edge. For focused local diagnosis, run node wework/e2e/desktop/run-checkpoints.mjs --short-conversation-only; the same check remains part of the regular e2e:desktop flow rather than a separate CI entrypoint.
The collaboration-runtime-reconciliation Core checkpoint runs independently and creates its own isolated cloud project, Issue, agent, and real Executor. A transparent WebSocket proxy holds actual completion events to cover snapshot-first, final-event-first, and terminal transactions contending on a real MySQL row lock. Each order checks the complete final text in the database, the Issue UI, and after reload. While a continuation's model response is held, it replays duplicate, reordered, unsequenced, and empty old completion events. Every replay waits for the real backend acknowledgement before asserting that the new turn, workflow, and execution capacity remain unchanged. The proxy only schedules transport; every component except the model service is real. Cleanup releases the model, transaction locks, and proxy connections.
The runtime-input-answer-persistence Core checkpoint answers real MCP elicitation forms in the main workbench pane and a temporary chat. The Executor, Codex app-server, MCP server, desktop automation transport, and work-list refresh stay real; only the model service is mocked. Executor request gates hold the accepted answer after the real response and hold the terminal continuation so the terminal history deterministically arrives before the work-list refresh completes. The checkpoint asserts that the full answer summary remains visible before and after the refresh. It also verifies that a transport failure and a stale executor rejection never mark the answer as accepted, and that archiving while acceptance is pending does not restore the answer.
The main desktop runner also supports execution through ordered checkpoints.
The checkpoints are mcp-apps, remote-device-onboarding, workspace-tabs,
cloud-project-creation, cloud-space-mention, priority-filter,
telemetry-consent, automation-lifecycle, project-automation,
project-assignment-notification, offline-local-project-space,
core-dsh-plugin-management, plugin-auto-update, plugin-workspace-publication,
project-ai-settings, model-routing, permission-modes,
runtime-input-answer-persistence, computer-use,
task-status-sync, task-board-association, core-task-flow, task-attachments,
cloud-git-worktree, cloud-worktree-capability,
cloud-worktree-create, cloud-worktree-queued-cancel, cloud-worktree-tools,
cloud-worktree-archive-restore, context-compaction, runtime-task-queue,
executor-stream-recovery, running-conversation-history,
codex-notification-isolation, split-workbench, native-window-startup,
native-window-chrome, renderer-storage, tray-lifecycle, window-lifecycle,
goal-lifecycle, supervisor-lifecycle, resilience, conversation-state,
temporary-chat, workspace-attachments, rendering-extensions,
change-request-status, claude-runtime, local-file-preview, local-harness,
harness-apps, browser-multi-tabs, embedded-browser, and
browser-toolbar-actions.
--segment <checkpoint> performs common startup and project
initialization, then runs only the selected checkpoint.
--from-segment <checkpoint> starts there and continues through every later
checkpoint. When upstream checkpoints are skipped, each checkpoint establishes
its own minimal fixtures instead of depending on tasks or UI state created only
by the complete flow. PR CI builds the smallest segment matrix for the changed
feature paths. Shared desktop infrastructure, merge queue, scheduled runs, and
ci:all still run the complete desktop suites. Registration, module paths,
execution modes, and Core/Cloud shard assignments now come from independent
wework/e2e/desktop/catalog/<checkpoint>.json files. The filename is the checkpoint
ID. checkpoints.mjs, the runner, and CI read the same declarations, so adding a
scenario does not edit shared registration lists or comma-joined shard lines.
order controls default execution order; ci.core / ci.cloud declare shard
and position, while each CI lane's order retains catalog order. Insert values
between existing ones; equal values sort by checkpoint ID without renumbering
other files. Core retains ten shards and Cloud retains eight. scenario names
the module and scenarioOnly runs that module alone. Composite checkpoints use
members; ciHost shares an existing checkpoint's CI lifecycle. Package upgrade
checkpoints remain owned by upgrade-suite.mjs.
A config change selects its CI owner, including the host of a composite member.
New scenario modules select their declaration automatically. Optional paths
shell globs add source coverage alongside existing path rules. Cross-feature
rules retain their priority in .github/scripts/desktop-ci/. Classifier contracts
live in .github/scripts/ci-classification-tests/, whose .sh files are discovered
by the existing test entry point.
Linux Core and Cloud jobs run at most four isolated checkpoints concurrently; actual concurrency also depends on the shard's checkpoint count. Each checkpoint owns its database, Redis, ports, and runtime directories, and Harness runtimes are extracted once before the shard starts. The former intensive-runtime and Collaboration groups therefore no longer serialize entire checkpoints. macOS checkpoints that require the native desktop foreground still use an exclusive resource lock.
split-workbench keeps its fixed three-second drag interaction assertion on a
dedicated Linux shard so competing full-stack instances do not change the benchmark's
CPU allocation. It retains all functional assertions and logs pointer movement,
pointer-up dispatch, and split readiness timings. Other functional shards still
allow up to four instances.
Core and Cloud matrix jobs also provide suite-level parallelism across runners.
The classifier provides 10 logical Core shards. Windows retains 10 runners;
Linux sets CORE_RUNNER_SCALE=1.5 and redistributes a full selection across
15 runners using historical durations. Partial selections scale with their selected
matrix, bounded by assignable checkpoints; selections without the history checkpoint
retain their original matrix. The two Core benchmarks keep dedicated runners.
Cloud uses 8 shards.
Combining short shards reuses container initialization, checkout, artifact
downloads, and runtime preparation without increasing checkpoint concurrency
within a runner. The two Core benchmark shards and three Cloud account-auth and
long-plugin-list shards remain separate. The concurrency limit must accommodate
the complete matrix in one scheduling wave; classifier regression tests enforce
both the shard budget and this constraint. Shared runner capacity can still
introduce external queueing.
Lint, unit-test, platform E2E, and desktop E2E summary jobs use
always() && !cancelled(): failures still produce a summary, while cancellation
stops superseded summaries from waiting for runners and blocking newer runs.
Cleanup and diagnostic upload steps retain their existing cancellation handling.
PR Wework renderer unit tests use 4 runners with the existing measured file balancing. Electron and auxiliary checks still execute once. Platform browser/API and provider-native E2E each use 8 complete, nonoverlapping logical shards, reusing shared builds with independent services and users per runner. Additional DingTalk protocol integration, knowledge, and attachment checks retain their designated shards. Historical Linux Core timing replay reduces the test-stage critical path from 614 to 478 seconds. This estimate excludes builds, preparation, and queueing; subsequent CI runs must establish actual savings. Additional runners increase preparation overhead.
Device folder selection uses native Executor home lookup and directory enumeration
for the default home_dir and ls_dirs commands, without spawning printenv or ls.
CI recorded process creation taking 6.2 and 8.1 seconds where the preceding run
took tens of milliseconds. Native queries remove this startup dependency while
preserving hidden directories, symlinks, output limits, and error handling.
Explicit custom commands retain command execution; the ten-second E2E assertion is unchanged.
Step timings from successful runs 39920301020 and 39920742402 separate test
work from setup overhead. In the first sample, non-test steps across 42 shards
consumed about 60 runner minutes, or 31% of shard execution time; setup overhead
was higher in the second sample. The final 18 shards remove 24 setup cycles.
Checkpoint timings from 39930854745 drive the final allocation: longest tasks
start first and cumulative work is balanced, while CLI and Electron preparation
remain shared. That run's workspace-tabs failed early because Issue creation
did not wait for readiness after navigating to the board. Its successful
116-second duration from 39920301020 supplies the estimate instead of its
incomplete failure duration. The click uses the existing clickWhenEnabled
with the default ten-second timeout and preserves all business assertions.
The 112 ordinary Core checkpoints total 10,246 seconds, distributed into eight
shards of 14 tasks with four concurrent workers each. Predicted durations are
323–332 seconds; a ninth shard only reduces the maximum to 320 seconds.
The 37 ordinary Cloud checkpoints total 5,769 seconds, distributed into five
shards with predicted durations of 291–339 seconds; a sixth shard only reduces
the maximum to 321 seconds. Two Core and three Cloud shards remain isolated.
These scheduling estimates exclude per-shard setup and runtime resource
variation and are not measured savings.
Background-terminal history verification reads the first visible xterm row's
aria-posinset, then waits for each native PageUp to strictly decrease that row
until reaching the start of history. Each operation retains the default
ten-second timeout, all twelve initial output lines are asserted, and native
PageDown restores the viewport. A single ten-second deadline cannot bound many
page operations: control latency under concurrent CI can exhaust it while
history remains intact. Focused regressions cover a slow control channel, stalled
scrolling, and missing history.
background-terminals runs alone first within its existing shard; the remaining
checkpoints then regain their configured concurrency. Native keyboard actions
avoid CPU contention from other full desktop environments while preparation is
reused, without another matrix job, a longer ten-second step budget, or fewer
history and native-input assertions.
Shards are balanced from observed CI durations; a new or
materially slower checkpoint requires rebalancing instead of
removing coverage or rerunning failures.
The Issue notification read scenario waits for the close button to disappear after
closing a drawer before interacting with the board. Closing commits after the exit
animation; clicking a covered card earlier can let the delayed close affect the
new Issue. Task lifecycle regressions cover delayed running snapshots for both the
same and a prior provider turn. A confirmed settled turn cannot restart from stale
metadata, even when its update timestamp exceeds a second-precision completion;
the executor can still confirm the start of a different new turn.
An earlier completion cannot settle a known newer live turn. Goal execution between
turns remains independent of turn completion and is confirmed by the executor.
Read-only board drag checks scroll the source card into view after grouping changes
and reloads. Owner lane-order checks and cross-column reload checks also scroll
each card into view before checking visibility. Windows can have a narrower available viewport, so priority and tag
columns cannot all be assumed visible. Permission and persistence assertions remain.
Tracked task activity composers also scroll into view before visibility checks.
Executor environment verification resolves real filesystem paths through Node to
compare Windows native paths with the Bash working directory correctly.
Notification tab navigation waits for the project URL to commit before recording
or comparing it; a rendered project title does not imply its asynchronous route
has committed. Project-route preservation and tab-reuse assertions remain enabled.
Foreground plugin installation pauses automatic page updates and synchronization
until completion, preventing stale inventory from triggering a full replay.
Cloud restoration retains the resolved socket endpoint. Runtime-token rotation
updates shared executor credentials while preserving the device connection and
RPC route, avoiding lost transcript requests during reloads. Traditional tasks read
rotated runtime tokens only while backend, socket, and session credentials match
the original connection; switched or disconnected sessions reject old tasks.
Goal restoration preserves the
known turn and rejects a different stale streaming history.
Background device refreshes retain app registration aliases that match the current
native runtime identity, so cloud comment addresses and native completion events
share one lifecycle. Registrations for older runtimes remain excluded.
If delivery finalization races the personal-task start transition, re-read the
Issue once after a version conflict and recompute the update, preserving completed
Issues. Web task joins resolve streaming state before reading messages so a stream
finishing during restoration cannot leave an old blank message snapshot.
Stand-in dispatch pins large create/send futures on the heap at their shared
boundary, keeping nested async state machines from overflowing Windows Tokio
worker stacks with exit code 0xC00000FD. Regressions enforce Clippy's
16 KiB large-future boundary
for manual, assignment, and scheduler entry points; existing
real desktop scenarios cover the complete execution flow.
Long history hydration polls use performanceSnapshot with collectMemory: false
to avoid forced GC on every poll; final memory assertions retain full sampling.
Timeout diagnostics include the last snapshot. After upstream #4146 bounded isolated
E2E fixture timers, permission modes measured about three minutes and fit alongside
the measured 25-minute Core 4. Remove the redundant shard, retaining the original ten
Core runners, real expiry assertions, and the 40-minute step limit.
Source project configuration no longer forces the Windows unelevated sandbox,
which rejects split writable roots; other permission and approval settings remain.
Windows source-project coverage therefore verifies outside writes can execute;
other platforms retain OS write-denial checks. All platforms still verify explicitly
authorized writes, native approval denial of private reads, and no private data
disclosure. The network probe declares a text response type so PowerShell does not
enumerate response bytes.
The subscription model fixture rejects every upgrade path with HTTP 426 and
handles ECONNRESET throughout the socket lifetime; other socket errors still
fail. Node regressions cover different paths, repeated resets, and unexpected errors, preventing
an unhandled socket error from crashing cleanup after scenario assertions pass.
Long scenarios are split into independent checkpoints as well as balanced shards:
The newly merged stand-in-project-enable checkpoint has no historical sample.
It shares preparation with the stand-in configuration flows in Core shard 3,
which now contains 15 tasks; other ordinary Core shards contain 14. The estimates
above exclude this new checkpoint.
plugin-account-authexpands into password sync, exclusive credential transfer, OAuth, DWS accounts, conversation QR, and GitHub checkpoints.plugin-uninstall-resilienceexpands into write coordination, lost response, unknown result, restart recovery, and reinstall race checkpoints.plugin-write-coordinationindependently checks API responsiveness during contention, post-commit ACK conflicts, and later sync recovery. Removal recovery checkpoints create another active installation so catalog refresh is tested alongside pending cleanup.harness-appsexpands into development, installation/runtime lifecycle, and multi-model routing checkpoints.streaming-textexpands into content, tool details, timers, scrolling, message order, and subagent checkpoints.
Each checkpoint establishes its own prerequisites in an isolated environment. The
group names remain valid for --segment; --from-segment starts at the group's
first checkpoint. CI schedules the individual checkpoints directly. Shards reuse
the same build artifact while reducing CPU contention between heavy workflows.
The long list still checks all 24 plugins, scrolling, and last-item selection.
plugin-toggle-persistence independently checks enable/disable persistence across
two restarts with one plugin, avoiding repeated synchronization of the whole list.
Linux Core/Cloud tests emit [runner-resources] JSON records at startup, completion,
and every 15 seconds, also saved as runner-resources.jsonl in the diagnostic
artifact. Web shards reuse this sampler with RUNNER_PROCESS_DIAGNOSTICS=1 to
record process IDs, parent IDs, states, elapsed times, and names without command
arguments. RUNNER_RESOURCE_LOG_PATH selects the log path; separate
e2e-process-diagnostics-* artifacts help diagnose commands that remain alive
after tests finish. Records include CPU utilization, load average, memory, available disk
space, Linux CPU/memory/I/O pressure, and cgroup CPU limits, memory peaks, and OOM
counters. Unreadable kernel metrics report unavailable. CPU utilization is the
average between samples; its initial value is null.
[desktop-e2e-schedule] logs list active checkpoints and actual concurrency on
each start and completion, making overlap visible independently of the configured
worker limit. Compare shard durations and these metrics after increasing
concurrency to identify CPU, memory, or I/O contention.
Every signal the runner delivers to an application process or process group is
logged as a single-line [desktop-e2e-signal] JSON record with an ISO timestamp,
covering process stops, process-group termination, crash-injection freeze/kill,
and forced in-scenario application kills. Chromium's Network service crashed
lines in app.log use runner-local time, so correlating both records attributes
each crash to harness teardown or leaves it as an unexplained event that needs
further investigation. When a checkpoint step fails, the runner also observes
the failed wait condition itself through the renderer's condition-polling waitFor
and captures failure-followup.json recording either the measured convergence
time (settled-after-failure) or that the condition did not settle within the
overall budget (condition-unsettled-within-budget), together with a UI snapshot
and workbench debug state. The convergence time is measured from the actual
step failure, while the budget bounds only the wall clock the follow-up itself
adds (the pre-follow-up diagnostics capture does not consume it); the recorded
observationBudgetMs and observedForMs show the observation window and how
long the condition was actually observed within it. When the failed step ran in
a detached window, observation and capture are routed to that window's control
client. Set WEWORK_E2E_FAILURE_FOLLOWUP_MS to adjust the
overall observation budget or 0 to disable the capture. The observation is a
condition-checking wait in the sense of the fixed-wait policy below: it returns
as soon as the failed condition settles and never sleeps a fixed duration. For
a diagnostic run that needs Chromium's own record of why child processes exit
(network service, zygote), set WEWORK_E2E_CHROMIUM_VERBOSE to a verbosity
level (1–9 or true for 1); the switch is off by default because the log
volume is significant. On Linux the verbose output is appended to app.log; on
Windows the scenario's --enable-logging=file wins over the stderr switch, so
verbose output lands in electron.log.
Ordinary fixed E2E waits must not exceed five seconds. Exceptions such as negative
observation across a complete polling interval, cancellation, or concurrent model
responses may use up to ten seconds with an explanation; observing a five-second
polling interval uses a 5.5-second window. UI, startup, and execution waits must
check the actual condition and return as soon as it is satisfied; their operation
timeouts are separate from fixed delays. Prefer the existing response release gate
when a model request must remain pending instead of using a long delay.
Drawer animation checks sample from before the action until movement is observed
and the track animation completes, then stop explicitly within the shared step
timeout. Command transport and rendering delays cannot exhaust a fixed 800ms
capture window. Movement, continuity, intermediate-frame, and reduced-motion
assertions remain intact.
The 2,200-delta Codex notification
isolation stress case and the long plugin auto-update checkpoint each have a
dedicated shard; the notification scenario uses a targeted 30-second render
budget without changing the shared 10-second UI timeout. CI first builds one Core Electron
application, Executor, and Codex artifact. Electron package preparation builds
the Harness runtime, Node execution runtime, and Executor concurrently; the
Harness preparation owns the single DSH application Vite build so the same
frontend is not compiled twice. Every Core and Cloud shard downloads and reuses
that artifact instead of rebuilding Vite, Electron, and Executor. Desktop shards
start only after the shared build succeeds and download it directly through the
GitHub artifact action. This prevents many runners from sitting idle while
polling during the build, allowing Lint, Tests, and Platform E2E to acquire
runners promptly without adding matrix jobs or dropping checkpoints.
Both the Rust gateway and Executor use debug profiles so E2E does not spend time
on unused release optimization. Test artifacts also disable dev-profile
debuginfo because diagnostics do not retain those symbols; generating and then
stripping them only extends a cold build. Rust builds
reuse both the main-owned Cargo target cache and sccache compiler units: the
target cache bounds PR and first-run latency, while sccache reduces incremental
compilation after dependency or source changes. Archiving strips Linux debug symbols only from the copied
artifact binaries, leaving the original build outputs unchanged while reducing
upload and download time across the shards. Source changes under backend-rs/**
or executor/** select the desktop target-cache warmup so main does not refresh
that cache only when a lockfile changes. Desktop E2E and its cache
warmup explicitly set WEWORK_EXECUTOR_PROFILE=debug so test artifacts do not
spend time optimizing the Executor. Release packaging leaves the variable unset
and continues to build the release Executor by default. Desktop E2E builds
skip the duplicate TypeScript typecheck that the parallel Lint workflow runs in full,
while retaining the real Vite and Electron artifact build; test coverage and the
type gate remain unchanged. The shared macOS Inspector and memory E2E build uses
a release Executor so memory measurements use optimized code. The macOS memory job keys its pnpm store from both
the workspace and Electron lockfiles, then installs offline so registry stalls
cannot consume the critical-path budget. Its large streaming Markdown response
uses a targeted 30-second completion budget while ordinary memory interactions
retain the shared 10-second timeout. The plugin suite requires an independent build
configuration and continues to run in parallel with the shared Core build.
Desktop shards use only the runtime tools already present in the immutable E2E
image; ZIP fixtures use Python's standard library, so the jobs do not restore
the full frontend dependency cache. Successful harness-app coverage keeps
milestone screenshots, and every run retains logs, state snapshots, and other
useful diagnostics. Before upload, CI removes only transient Chromium caches
that cannot help reproduce a failure. Uploads disable redundant compression for
PNG and other already-compressed evidence so artifact archiving does not extend
the pipeline tail.
The Backend Rust E2E image producer, desktop consumers, and warmup must use the
same content digest, covering Cargo manifests, the lockfile, source, examples,
and e2e-tests.yml, which defines the artifact layout. The image must contain
both wegent-backend-rs and attachment-s3-e2e. Missing artifacts fail restoration
and release the temporary container; an incomplete cache is not a fallback.
CI cache policy runs test-backend-rs-e2e-runtime.rb, which executes the actual
workflow build, publish, and restore scripts to check this contract.
In Core shard 6, harness-app-models holds the real additional-token handoff,
asserts that the running tab is not published yet, then checks close, reopen,
model calls, and route release. A host running snapshot does not mean renderer
handoff is complete. Management refresh, open, and retry must not bypass the
installation's pending lifecycle. On failure, compare delivery counts in
harness-model-token-handoff.json with the UI snapshot rather than increasing
waits or weakening the early-publication assertion.
A retained management page can leave a stopped runtime's URL registered after
restoring an old running snapshot. Starting again invalidates that registration
before creating a new tab. An existing tab or pending operation still owns its
registration and must not lose it to a stale management snapshot. The checkpoint
checks for no stale iframe immediately after Start and no running iframe before
the additional-token handoff finishes.
Merge queue validates
the final commit that enters main, so Tests, Lint, Platform E2E, and Wework E2E
do not repeat the same validation after the merge through a push main trigger. The
mapping is maintained in feature-local catalog files. Declare a new checkpoint's
module, execution mode, CI shard, and source paths in its own file, then add
focused assertions to the relevant classifier contract module. Update affected
matrix expectations when cross-feature coverage changes; shared runner lists,
shard lists, and test entry points no longer need edits. The classifier rejects a
checkpoint that exists only in the runner without CI catalog coverage, preventing
locally runnable coverage that GitHub CI never invokes. Segment commands are also
useful for focused local iteration:
pnpm --filter wework e2e:desktop -- --segment automation-lifecycle
pnpm --filter wework e2e:desktop -- --segment local-harness
pnpm --filter wework e2e:desktop -- --segment model-routing
pnpm --filter wework e2e:desktop -- --segment window-lifecycle
pnpm --filter wework e2e:desktop -- --from-segment window-lifecycle
pnpm --filter wework e2e:desktop -- --segment temporary-chat
pnpm --filter wework e2e:desktop -- --segment workspace-attachments
pnpm --filter wework e2e:desktop -- --segment claude-runtime
automation-lifecycle independently covers automation creation, immediate
execution, pinning and continuing an existing task, and scheduled continuation.
model-routing independently covers all six local protocol-switch directions,
cross-provider retry, the vision sidecar, and the local model protocol matrix.
Both checkpoints establish their own minimal fixtures, so they no longer extend
the critical path for task creation, follow-up, and background plans in
core-task-flow. An unsegmented complete desktop run still executes these
scenarios, so splitting them does not reduce coverage.
temporary-chat creates an independent local project and real Codex ephemeral
thread, holds a follow-up response open, verifies that the user message appears
above the Thinking indicator, and confirms that the temporary-chat content is
restored from the runtime conversation cache after switching the main
conversation away and back.
claude-runtime uses the real Electron Wework application, Backend, local
executor, and remote executor to verify Claude Code creation, follow-up, and
cancellation on both local and remote devices. It also confirms that remote
execution uses the target device's own Claude Code binary and checks visible
DOM state so the sidebar and composer stop showing running after completion;
the hidden sidebar preview retained by the layout is excluded from visible
state assertions.
local-harness uses the real OpenCode, Claude Code, and Kimi Code CLIs pinned
by .github/claude-code-cli/package-lock.json; argument-recording shell
fixtures are not valid substitutes. The scenario must assert real requests
from every CLI through the Wework local model proxy. Kimi Code starts as an
interactive TUI, so its first prompt is injected only after PTY output contains
the readiness marker. Writing during terminal capability negotiation loses the
input.
The plugin desktop suite reuses the same segment options while keeping its
separate Codex Home initialization environment. Its ordered segments are
plugin-lifecycle, skill-mention-rendering, and
sites-plugin-auto-install. Each segment establishes the minimal plugin
fixture it needs, so it can run alone or continue through the later plugin
features. plugin-lifecycle also covers: the composer plugin picker no longer
listing a plugin after uninstall, and unmatched assistant need_login /
connector_auth_required resume text not opening a local auth card.
pnpm --filter wework e2e:desktop:plugins -- --segment skill-mention-rendering
pnpm --filter wework e2e:desktop:plugins -- --from-segment skill-mention-rendering
Ordinary desktop-runner UI steps time out after 10 seconds by default, so one
failed step does not always wait for the former 120-second limit. Control
commands and wait helpers accept timeoutMs for genuinely slower special
steps; startup, workbench recovery, and the model protocol matrix keep their
dedicated limits. Set WEWORK_E2E_STEP_TIMEOUT_MS to temporarily adjust the
global default for ordinary steps in a slower diagnostic environment.
Role settings load asynchronously, so source management uses clickWhenEnabled before clicking its entry. Catalog saves and restores first use the shared waitForCatalogRestart to handle the actual confirmation and verify that a new Codex process serves the catalog after persistence, then assert that the editor closes. A successful IPC request alone does not prove restart completion. These regressions retain the 10-second budget without reruns or expanded timeouts.
The main desktop flow also covers pasting or dropping ordinary files and folders from Finder. The composer must render file and folder path chips without creating an attachment badge. The request sent to Codex must contain the matching absolute paths without inlining file contents. This decision depends on whether the native desktop transfer resolves a file path; the composer must not switch to attachment upload early only because its current workspace is marked as remote. The top quick-send window uses the same rule and reads bytes only for image attachments. Both scenarios use ordinary small files. For focused local diagnosis, run node wework/e2e/desktop/run-checkpoints.mjs --pasted-workspace-paths-only or node wework/e2e/desktop/run-checkpoints.mjs --dropped-workspace-paths-only.
Following the cc-switch conversion boundary, the mock strictly validates what reaches the model side: authentication, model ID, stream settings, message history, tool choice, shell tools, and either the apply_patch Lark grammar or its function wrapper. Any incorrect field returns a non-2xx response and fails the test. The desktop test stores a follow-up screenshot for each interface plus the complete model-requests.json; GitHub Actions uploads desktop diagnostics on both success and failure.
Plugin boundary regressions are integrated into existing checkpoints, keeping the Cloud matrix at eight shards. After its original write-coordination checks, plugin-write-coordination verifies installation, manual updates, and automatic update recovery using the real MinIO, Backend, Executor, and fault proxies already running in that scenario. Run it independently with pnpm --filter wework e2e:desktop -- --segment plugin-write-coordination; these boundary checks do not need additional jobs.
Operation coverage includes disconnection, lost committed responses, failed initial package publication, retaining the working version after an upgrade failure, and continuing healthy updates after a failed item. Faults affect real transports without replacing business responses. SHA-256 request ID fingerprints distinguish logical operations from transport attempts without recording headers or credentials. Assertions check the UI, database, device versions, and actual skill contents.
Before its original lost-response uninstall check, plugin-uninstall-lost-response runs the catalog boundary groups for source failure continuation, late responses, account and device cache isolation, detail and image failures, and blocking automatic writes from incomplete inventories before resuming after recovery. Run these groups before publishing other plugin fixtures so the real empty-catalog and exact-membership assertions remain valid. The groups share the scenario's services and retain their own cleanup and evidence. Run them with pnpm --filter wework e2e:desktop -- --segment plugin-uninstall-lost-response; CI covers this checkpoint too. Each group saves JSON evidence and screenshots; any failed assertion makes the checkpoint exit nonzero.
The environment needs Rust, Electron build dependencies, and a real Codex binary. The runner finds codex on PATH by default; an installed or prepare:codex-prepared real binary can also be selected explicitly:
CODEX_BIN=/absolute/path/to/codex pnpm --filter wework e2e:desktop
When binaries are not configured, the desktop runner first runs
ai:verify:electron:build, validates the packaged real Electron application and
its bundled Executor, and then starts the selected scenario. Optional
WEWORK_E2E_EXECUTOR_BIN and WEWORK_E2E_APP_BIN must be set together to reuse
already-built real binaries. A supplied application must be built with the
desktop E2E Vite environment variables. The lifecycle scenarios share one
application launch to control CI duration. Test artifacts, captured model
requests, and failure diagnostics are stored in
wework/test-results/desktop-e2e/. Each scenario retains logs, screenshots,
requests, and UI state while removing copied application bundles, Executor
homes, workspace fixtures, test archives, extracted runtimes, and every other
rebuildable top-level test environment. electron-user-data retains diagnostic
state only: materialized managed-components, managed-runtimes, DSH and
Harness profiles, and Electron caches are recursively removed from both the
main instance and nested plugin-development instances. The runner also compacts
inactive result directories left by interrupted runs so repeated local
execution does not continuously consume disk space.
The Linux CI plugin-development shard starts Electron-managed Executor processes
under GDB and records all thread stacks on native crashes. A startup check verifies
that the IPC file descriptor reaches the debugged process. Standalone MCP and schema
queries retain their stdout protocol. Stack diagnostics omit arguments and locals.
Desktop E2E Electron logs also retain redacted renderer errors. Git worktree and
conversation-index replacement failures record Git stderr or the original OS error
code to distinguish page timeouts from failures in the underlying execution.
Windows plugin installation diagnostics also record application asset directory
state, link targets, and file counts before and after pnpm operations. Static asset
read failures record the parent directory's link state to locate missing resources.
Windows component compositions and managed plugin packages own independent resource
files so pnpm cleanup cannot traverse directory links into shared components. Layout
fingerprints and copy-version stamps rebuild older layouts. Plugin-development E2E
checks that starting the isolated window preserves main-application assets and that
Windows package asset directories do not link to shared components.
Windows worktree commands set GIT_COMMON_DIR from the existing .git and
commondir parser so checkout children find shared objects before loading repository config.
Paths passed to Git omit the Windows verbatim prefix, allowing its slash-separated
/config suffix to be normalized by the Windows path layer.
Worktree removal, restoration, and snapshot-ref cleanup use --git-dir . inside
the common Git directory, avoiding Git's explicit-directory length rejection before
configuration loads. Worktree target paths remain absolute.
Windows conversation indexes use POSIX replacement semantics with FileRenameInfoEx:
existing readers retain the complete old index, and new readers see the newly published index.
The destination parent is canonicalized to retain the long-path prefix for the direct
Win32 call, including initial index creation.
Native CI covers repeated replacements with open readers and replacements beyond MAX_PATH.
Plugin cache mutations restart the Codex app-server, so synchronization waits for both AI tasks and background terminals to finish. Regression coverage verifies deferred updates while a process runs and publication after its native exit notification. Desktop coverage retains output-reading and process-stopping assertions after a window reload. Windows fixtures use cross-shell terminal commands and verify a completed output line; settings controls are scrolled into view. Git fixtures disable automatic line-ending conversion, and queue/sidebar fixtures explicitly select the test model without weakening behavioral assertions. CI verifies the development Electron binary during setup so its first import does not download it inside a scenario.
Remote collaboration initializes the execution environment catalog through the real
Backend and verifies authorization for the remote device before opening the Agent form.
Native model catalog initialization does not consume the form's 10-second UI step budget.
On desktop startup failures, scenario-state.json records application launch and exit
times and exit codes. Windows also retains electron.log to diagnose native failures
before the renderer connects.
Migration fixtures isolate workbench data through HOME and preserve the operating
system's USERPROFILE. The controller acknowledges each action before polling for
the next command, keeping pending long polls out of the result acknowledgement path.
System proxy scenarios and the initial task-status synchronization request use the
model-stage budget for model requests and retain 10-second ordinary UI steps.
Initial tasks and follow-up messages reuse sendPromptUntilScenarioRequest,
registering request listeners before sending with the shared 30-second model-request limit.
Background guidance uses the same model-stage budget for real tool output and subsequent model
requests. A native tool can yield a still-running session after ten seconds; this tool phase
must not use the ordinary UI-step limit.
The message-order scenario waits for the current turn's SSE response to finish sending with the model-stage budget, then uses the unchanged ten-second UI budget for its message content. The recorded response number must match the current turn. HTTP diagnostics retain request start, finish, and duration so Codex execution delays are not mistaken for missing virtual rows.
The stream-recovery scenario selects its registered fixture model before the first send;
the composer appearing does not establish model-catalog readiness. It still verifies that
streaming continues during renderer reload and restores the complete output.
The shared Enter-send helper and model matrix reuse the same send-control readiness check.
After filling a draft, wait for the control to become enabled with the original ten-second step
budget, then send Enter once. An available editor or stable composer text does not establish
restored model-catalog readiness after reload. The model request and
send action are awaited together so either failure follows the normal diagnostics and cleanup
path instead of an unhandled Promise rejection exiting Node without a failure snapshot.
Model-request waits stop polling when their budget expires, leaving no active poll timers.
The ordinary send-control wait retains the 10-second step limit.
Restoring native model-catalog defaults temporarily unmounts model rows while refreshing.
The scenario observes the stable page until the original row returns without its customization
marker, then verifies native data and the persisted override file.
The overload cancellation phase selects its fixture model independently for the new conversation.
When a user resumes and completes a Goal during overload backoff, the executor processes queued new-turn events before applying the latest Goal snapshot. A new turn still running must deliver its own terminal event. Focused regressions cover completed, in-flight, paused, and cleared Goals, preserving the current turn identity without an extra Goal activation or turn start. Manually driven fake app-server processes use independent temporary Homes and private state outside the global registry, preventing concurrent skill reloads or executor-wide shutdown from interfering with another test.
The device artifact declaration scenario uses one Node invocation to generate its file and execute the real declaration command, avoiding Unix heredoc syntax in Windows PowerShell. Only a successful declaration emits its completion marker, which the model must receive before finishing. Unique-artifact, exact-content download, and native-preview assertions remain enabled. Local command verification covers PowerShell, Unix shells, spaces and Unicode in paths, and declaration failure propagation.
In run 40623393090, every Windows Core 4 checkpoint passed, but startup and cleanup exhausted the 40-minute step limit. Move the measured six-minute board sorting checkpoint to the existing roughly 22-minute Core 8 shard. Place permission modes in Core 4 and remove the extra shard. Retain the original ten Core runners, every checkpoint, the 40-minute limit, and exclusive latency benchmark shards.
The stand-in settings scenario uses the existing clickWhenEnabled action to wait for its sidebar entry after switching collaboration tabs or reloading. A visible tab does not establish that its asynchronous page has mounted.
Action execution and result acknowledgement are separate: an acknowledgement failure must not
produce a fabricated failed-action result. Control stages use the existing debug-log snapshot
capture. Result transport records client duration, event-loop lag, page visibility, and server
receipt time; the server records whether the command is still pending when its result arrives.
No action contents or authentication values are logged. E2E Electron logs retain loopback request paths and
network error codes without headers or query parameters. Windows failure artifacts include
windows-network-diagnostics.json with TCP state and process connection counts, excluding addresses,
to diagnose Winsock resource exhaustion.
Task-board bulk actions confirm that the blank conversation is active before each input;
the editor node alone is insufficient. Goal restart fixtures observe one running task and
one queued task before releasing the model response occupying the single execution slot.
Work-list refreshes preserve the device owning an opened workspace so an earlier response
cannot replace a newly selected cloud workspace.
Workbench and conversation diagnostics share a 100ms publication scheduler. Continuous state updates must not cancel a pending publication, and each publication reads the latest state. Disabling diagnostics or unmounting cancels pending work so an inactive pane cannot overwrite the current snapshot. A failed conversation-title isolation assertion retains its assistant-count baseline and the title, lifecycle, and message summary from each poll.
Manual conversation renames emit the existing runtime.task.title.updated event after persistence so other workbench consumers immediately receive the private title. Terminal history diagnostics record keyboard and actual viewport metadata without terminal contents. Native input waits for a trusted keyboard event at the exact target and a successful host response, rather than fixed 16ms settling timers. Missing events retain the 10-second step budget, and listeners are removed on success or failure. For native CPU traces, point WEWORK_E2E_RENDERER_CPU_PROFILE_DIRECTORY to an absolute directory. Isolated E2E saves renderer-*.trace.json every 20 seconds to identify renderer long tasks.
Diagnostics record project-selection
reducer actions and IDs, draft scopes and parent acknowledgement revisions, Codex app-server
process IDs and initialization exit states, and failed Windows process-tree termination states.
Configuration normalization and replacement record file size and the presence of model and built-in OpenAI URL fields; replacements also record their call site, without configuration contents.
Only a missing configuration file permits empty initialization; other read failures stop startup and preserve the existing file. A regression uses invalid UTF-8 to trigger a real read error and verifies that the original bytes remain unchanged, preventing normalization from silently clearing model addresses.
codex-invalid-launch-cwd records isolated configuration fingerprints and fixture-field matches before launch, around executor restart, and before sending. It also reads native configuration-layer sources and field presence to distinguish disk changes from runtime loading failures.
Codex app-server restarts and configuration changes hold the admission lock until the old process exits before admitting a new process.
Failure artifacts include codex-sqlite-diagnostics.json with isolated database file sizes,
read-only integrity results, and SQLite error codes; conversation tables are never queried.
Failed executor compatibility queries record elapsed time, exit code, signal and termination status
without child output, command paths or environment variables.
Desktop fixtures bind the workspace root to their temporary Home and remove other inherited
workspace path overrides, avoiding stale test or personal worktrees. Turn preparation failures
record their error code and reason instead of leaving only a UI timeout.
Executor configures loopback NO_PROXY before creating the Tokio runtime, avoiding
process environment mutation on asynchronous connection threads. The startup scenario
verifies that Executor becomes ready before Core DSH starts and that DSH binds a valid port.
The blocked-proxy startup scenario verifies that the workbench starts without waiting
for Codex network access, then releases the proxy and waits for local plugin registration
before clearing the proxy setting, avoiding cancellation of in-flight initialization.
Core DSH binds port 0 directly and lets the OS assign its listening port.
Electron reads the actual DSH address before probing the host, Executor, Terminal,
and application endpoints. It does not discover a port by opening and closing a
temporary listener, avoiding collisions during concurrent startup.
Linux cloud-test services, including backends and databases, publish ports before
startup. The shared allocator reads /proc/sys/net/ipv4/ip_local_port_range and
selects bindable, unprivileged ports outside that kernel dynamic range. The port
registry keeps parallel checkpoints separate, while excluding the dynamic range
prevents Core DSH and other port 0 listeners from claiming ports during service
initialization.
Before upload, GitHub Actions repeats the cleanup and excludes
managed-components at any depth from the Artifact path so a teardown failure
cannot upload the complete Web and WASM component payload from either the main
instance or a nested plugin-development instance as diagnostic evidence. Both
the top-level cleanup and Artifact paths also exclude test .zip archives.
Diagnostic Artifacts use standard compression and should normally remain below
20 MiB. They should contain logs, screenshots, requests, UI state, and necessary
Electron persistence only, never rebuildable applications, runtimes, Executor
homes, workspaces, test archives, or component directories.
The local runner writes complete stdout and stderr from the prerequisite
Electron and Executor build to
wework/test-results/desktop-e2e/desktop-build-<pid>.log. The terminal shows
only the build stage, a liveness message every 30 seconds, elapsed time, and the
log path so package installation, compilation, and signing output do not bury
checkpoint results. On build failure, the terminal also prints a bounded tail
of the log. Test output remains live after the scenario starts.
ai:verify:electron:build shares immutable Harness Runtime archives across worktrees. The default archive cache is ~/Library/Caches/wegent/harness-runtime on macOS, ${XDG_CACHE_HOME:-~/.cache}/wegent/harness-runtime on Linux, and %LOCALAPPDATA%\wegent/harness-runtime on Windows. Materialized development runtimes remain under wework/node_modules/.cache/harness-runtime-dev in the current worktree so mutable state is not shared across source trees. Set WEWORK_HARNESS_RUNTIME_ASSET_CACHE_ROOT to override the archive cache. If a build only sets the legacy WEWORK_HARNESS_RUNTIME_CACHE_ROOT, that value is translated into an archive-only cache override and does not move the current worktree's materialized runtime.
Before launching Electron, desktop E2E removes inherited task, IPC, Node, and Harness runtime variables so a development Wework or Codex session cannot leak personal runtime paths into the test application. To select a Core DSH runtime explicitly for focused diagnosis, set WEWORK_E2E_HARNESS_RUNTIME_ROOT; the runner validates that directory and passes it to the test application as WEWORK_HARNESS_RUNTIME_ROOT. Do not pass WEWORK_HARNESS_RUNTIME_ROOT directly to the test command.
On macOS, desktop E2E injects the test-only WEWORK_E2E_BACKGROUND_WINDOW=1 setting by default. It keeps the Electron main window hidden, prohibits application activation, and hides its Dock icon. Background throttling is disabled for the hidden WebView, so DOM control, timers, and snapshots continue to work. Set WEWORK_E2E_BACKGROUND_WINDOW=0 explicitly when foreground window inspection is required. This setting affects desktop E2E only; normal development and production launches are unchanged.
The cloud-project scenario starts a real Backend, Redis, and a real Executor registered as a remote device. It exercises real authentication, device RPC, task persistence, and project deletion while covering project creation, task execution, conversation restoration, follow-up, and project removal. The scenario also verifies all three model protocols through the Backend proxy for cloud Model CRDs, plus local-executor use of Codex and cloud models under the same connected account. Only provider model endpoints are simulated; Backend HTTP and WebSocket APIs must not be mocked. To shorten cold startup, the Executor build runs in parallel with Backend, Redis, and database preparation, while remote Executor registration runs in parallel with the Electron application build. Application startup still waits for both prerequisite groups to finish. Project cleanup must wait until the task is no longer running; rendered assistant text does not mean the final task state has been persisted. Python 3.11, uv, and redis-server are required to run this scenario.
The hybrid Backend launcher gives Python Uvicorn a 2,400-second upstream HTTP
keep-alive, longer than the workflow's longest 35-minute hybrid E2E job.
brz-http-gateway 0.1.4 builds a hyper-util connection pool without installing
its idle-connection timer. If Uvicorn expires an idle socket first, the gateway
can reuse that upstream-closed connection and incorrectly return 502 fallback upstream unavailable. Bounding the upstream lifetime beyond the job prevents
that stale-socket window in this local hybrid launcher. The setting affects only
the local Rust-to-Python fallback connection and does not disable keep-alive
between external clients and the gateway.
Before validating local-executor models for a connected account, the cloud scenario selects its isolated directory through the current Projects → Local project entrypoint and confirms the name in the local-project creation dialog. Desktop E2E coverage must follow this primary product flow instead of relying on the removed existing-project test entrypoint.
The plugin-account-auth scenario uses shorter scheduling intervals only when
an E2E reconciliation marker is configured and emits one precise retry signal
after the test changes credential state. Production retains the 15-second
automatic synchronization period and 60-second failure backoff. The DWS manual
revocation assertion no longer sleeps for a fixed 32 seconds. It waits for two
completed reconciliation markers from the real Executor while continuously
asserting that the grant remains revoked, so the optimization removes idle time
without dropping account migration, cloud execution, or revocation coverage.
The GitHub Actions Executor E2E job loads a prebuilt Docker image after restoring Python, Node.js, and Playwright caches. It must first remove unused hosted-runner SDKs (.NET, Android, GHC, and CodeQL) and print disk usage so image extraction has stable headroom. The cleanup must not remove the running MySQL or Redis service images.
The plugin scenario dynamically creates an isolated local Codex marketplace and a plugin with a Skill under the test-results directory. It then uses the real Electron renderer, Executor, and Codex app-server to verify marketplace discovery, installation, the install-time local authorization dialog, insertion of the plugin reference into the chat composer, unmatched resume auth text not opening a local auth card, composer filtering after uninstall, and uninstallation. It neither reads the user's Codex home nor mocks plugin APIs; marketplace data, plugin cache, and installation state remain inside the isolated test directory. Screenshots are retained for the critical stages, with application, Executor, and UI snapshot diagnostics retained on failure.
The memory scenario is macOS-only. It keeps the bottom Terminal mounted, executes a development task through a real Codex tool call, then streams a long response containing Markdown, tables, and TypeScript code into the real Electron renderer. The test first waits for the renderer-process memory baseline to stabilize, then samples the aggregate physical footprint of associated Electron renderer processes every 500 milliseconds. Each sample also forces renderer garbage collection through the Electron DevTools Protocol and reads actual JS heap usage. It writes the samples, DOM node counts, active-assistant content summary, and aggregate metrics to memory-growth.json; the gate does not include the main Wework process. The default gates limit physical-footprint peak growth to 384 MiB, settled growth after completion to 232 MiB, the full physical-footprint range within the settled window to 16 MiB, and the post-GC JS heap peak across the workload to 200 MiB. The DOM gate checks the settled window after virtual-list convergence and allows at most 576 additional nodes relative to baseline by default. Transient peaks during streaming remain in the diagnostics but do not treat pre-convergence rendering as a leak. The limits can be adjusted with WEWORK_E2E_MEMORY_MAX_PEAK_GROWTH_KIB, WEWORK_E2E_MEMORY_MAX_SETTLED_GROWTH_KIB, WEWORK_E2E_MEMORY_MAX_SETTLED_DOM_NODE_GROWTH, and WEWORK_E2E_MEMORY_MAX_JS_HEAP_BYTES. For focused very-long-stream reproduction, use WEWORK_E2E_MEMORY_SECTION_COUNT and WEWORK_E2E_MEMORY_CHUNK_DELAY_MS to configure response sections and chunk delay. The completion budget remains fixed at 30 seconds and cannot be relaxed through an environment variable.
The concurrent-memory scenario is also macOS-only. It creates and holds 10 Responses streams at the same time, samples the process-group physical footprint for the Wework main process, Electron renderer/GPU/network processes, Executor processes, and the Codex app-server, and writes the evidence to concurrent-memory.json. Relative to the stable baseline, both the peak and active settled plateau may grow by at most 320 MiB, while the settled sampling window may vary by at most 64 MiB. The limits can be adjusted with WEWORK_E2E_CONCURRENT_MEMORY_MAX_PEAK_GROWTH_KIB, WEWORK_E2E_CONCURRENT_MEMORY_MAX_SETTLED_GROWTH_KIB, and WEWORK_E2E_CONCURRENT_MEMORY_MAX_SETTLED_SAMPLE_RANGE_KIB. The scenario also switches between the first and last tasks and waits for each task's prompt content to reappear.
Responses API Mock
wework/e2e/utils/mock-response-api-server.mjs provides a real HTTP service that validates local-model capability probes:
POST /v1/responses: returns non-streaming Responses API JSON.POST /v1/responsescapability probes: returnstext/event-streamtool-call events such asresponse.output_item.addedandresponse.output_item.done.POST /v1/responseswithstream: true: returnstext/event-streamevents includingresponse.created,response.output_text.delta, andresponse.completed.POST /v1/chat/completions: validates and returns a Chat Completions function tool call.POST /v1/messages: validates and returns an Anthropic Messagestool_useblock.GET /captured-requests: reads captured requests.POST /clear-requests: clears captured requests.GET /health: CI health check.
The mock enables CORS, so the Wework page can call it with a real browser fetch. For model connection tests, use this base URL:
http://127.0.0.1:9998/v1
External Upstream Mocks
Wework E2E also starts two local loopback upstream mocks. They replace only services outside Wegent; they do not replace Wegent Backend, Executor, Codex, /api/sites, /api/apps/installed, or connector runtime APIs.
wework/e2e/utils/mock-sites-upstream-server.mjs simulates the Sites project API and returns Sites or Mini Programs according to app_type:
GET /api/v1/projects/search: returns deterministic projects and supportsusername,limit,sitename, andcursor.POST /api/v1/projects/deploy/network: updates project network visibility.POST /api/v1/projects/update: updates project name.POST /api/v1/projects/del: deletes a project.GET /captured-requests,POST /clear-requests,POST /reset,GET /health: assertion and reset helpers.
To cover the Sites path through a real Backend, start Backend with the following environment variables. These variables must be passed to the Backend process; passing them to Vite or the Playwright page does not configure Backend.
SITES_API_BASE_URL=http://127.0.0.1:9997
SITES_API_TOKEN=e2e-sites-token
Example:
cd backend
SITES_API_BASE_URL=http://127.0.0.1:9997 \
SITES_API_TOKEN=e2e-sites-token \
uv run uvicorn app.main:app --host 127.0.0.1 --port 8000
wework/e2e/utils/mock-connector-upstream-server.mjs simulates external services that connector apps can target:
GET /api/tickets/{id}: acts as an HTTP connector upstream.POST /mcp: provides minimal Streamable HTTP MCP JSON-RPC behavior forinitialize,tools/list, andtools/call.GET /captured-requests,POST /clear-requests,GET /health: assertion and reset helpers.
Connector app test data can use:
HTTP connector base URL: http://127.0.0.1:9996/api
MCP URL: http://127.0.0.1:9996/mcp
An HTTP connector fixture can use:
{
"slug": "ticket-http",
"name": "Ticket HTTP API",
"description": "E2E HTTP connector upstream",
"enabled": true,
"visibility": "all",
"allowed_roles": [],
"auth_type": "none",
"transport": "http",
"mcp_url": "http://127.0.0.1:9996/api",
"oauth_scopes": [],
"provider_headers": {},
"tool_allowlist": ["get_ticket"],
"http_tools": [
{
"name": "get_ticket",
"description": "Get one mock ticket",
"method": "GET",
"path": "/tickets/{id}",
"input_schema": {
"type": "object",
"properties": {
"id": { "type": "string" },
"expand": { "type": "boolean" }
},
"required": ["id"]
},
"argument_locations": {
"id": "path",
"expand": "query"
}
}
]
}
An MCP connector fixture can use:
{
"slug": "docs-mcp",
"name": "Docs MCP",
"description": "E2E Streamable HTTP MCP connector upstream",
"enabled": true,
"visibility": "all",
"allowed_roles": [],
"auth_type": "none",
"transport": "streamable-http",
"mcp_url": "http://127.0.0.1:9996/mcp",
"oauth_scopes": [],
"provider_headers": {},
"tool_allowlist": ["search_docs"]
}
Automation Bridge
In test mode, Wework exposes a frontend control bridge at window.__WEWORK_E2E__. The bridge is installed only when import.meta.env.MODE === "e2e" or VITE_WEWORK_E2E=true; normal development and production runs do not enable it by default.
Isolated real Electron verification can dispatch path paste or drop events to the composer with ai:verify paste-paths or ai:verify drop-paths. The --value argument accepts a JSON array whose entries contain uri and name; folder entries also set isDirectory: true:
pnpm --filter wework ai:verify paste-paths \
--session /absolute/path/to/session.json \
--selector '[data-testid="chat-message-input"]' \
--value '[{"uri":"file:///tmp/context","name":"context","isDirectory":true}]'
Replace paste-paths with drop-paths in the example to verify Finder drag and drop.
Available methods:
isDesktop(): returns whether the app is running in Electron.getRuntimeConfig(): reads the current runtime config.getRoute(): returns the current route with the app base path removed.navigate(path): changes route through frontend history and dispatches navigation events.waitForTestId(testId, options): waits for adata-testidto appear.queryTestIds(prefix): lists currentdata-testidvalues, optionally filtered by prefix.setAuthToken(token): stores a real auth token.clearAuthToken(): clears the auth token.clearStorage(): clears local auth state and browser storage.
The desktop E2E build additionally injects VITE_WEWORK_DESKTOP_E2E_CONTROL_URL. Only when E2E mode and this URL are both present does the frontend poll a local loopback controller for click, fill, and wait assertions; normal development and production builds have no controller endpoint. The controller drives real WebView DOM events and does not replace task, model-selection, Executor, or Codex implementations.
When a built-in action does not handle a command, the public controller delegates it through @extensions/desktop-control to a product extension. Without a product extension, unknown actions fail explicitly; public automation does not recognize concrete product protocols.
The control channel binds commands to the window's current client. The frontend acknowledges each result before starting its next long poll, keeping command requests out of the result acknowledgement path. A ready event after a WebView reload confirms transport reconnection, not that the application shell has mounted. Notification entry uses clickWhenEnabled for the button and category, retaining the shared ten-second UI step budget. When fill targets a Lexical editor, the controller uses the editor's exposed value setter so the React/Lexical state is actually committed; do not replace it with raw DOM insertion.
Failure diagnostics retain delivered commandHistory in scenario-state.json, including queuedAt, deliveredAt, startedAt, resultReceivedAt, and failedAt. Compare these timestamps to distinguish queueing, start acknowledgement, and result return delays. The last interval includes both UI execution and result transport; a timeout alone does not prove a missing UI element. A late result for a finished command still returns 404; it only adds lateResultAt and a console timeline without the result body, never converting a failed command into a pass.
Paging diagnostics in background-terminals capture the real xterm buffer position alongside accessibility row positions, window focus, and native key modifiers. Successful initial and restored history checks save background-terminal-keys-initial.json and background-terminal-keys-restored.json; failures retain nativeTerminalKeys in workbench-debug.json. Diagnostics only read positions and event metadata: they do not change scrolling or retain terminal text, and the read-only buffer bridge exists only in E2E builds. Assertions compare buffer positions around the same trusted native key, then wait for the accessible top row and verify the first twelve output lines. Accessibility rows can lag behind the buffer, so an older row number alone is not the real position immediately before a native key executes.
responseFinishedAt records when the control server finishes writing a command response. desktopControlTransport in workbench-debug.json provides a bounded renderer timeline for receiving response headers, decoding commands, acknowledging start, completing actions, and acknowledging results, including window visibility and focus. Compare both sides to locate delivery-to-start delays without extending timeouts or replaying commands. Diagnostics omit selectors, input values, and result bodies. reply-predictions enables existing Composer submission diagnostics only in its isolated environment to distinguish native Enter delivery, input policy, and business submission gates.
Executor-owned turns such as supervisor corrections retain the previous completion internally for ordering and stale-provider recovery, but task wire summaries explicitly return completedAt: null while running=true. Omitting the field cannot clear the old value in patch-merged frontend caches. supervisor-lifecycle checks running state and this wire boundary while deliberately holding the model response. The frontend still rejects active snapshots carrying stale completion data and requires a genuinely new running projection to advance time. Do not relax these stale-state guards or hide autonomous admission behind frontend state activated only after model output.
The desktop controller uses Electron webContents.capturePage() for native renderer screenshots. The capability is available only when VITE_WEWORK_E2E=true; selector captures crop the native image by the element bounds, so diagnostics preserve the actual Electron rendering.
Switching models in the same conversation can cause Codex to issue an internal context-compaction request. The desktop task-flow E2E loopback Responses service identifies and responds to these requests through client_metadata.x-codex-turn-metadata.request_kind === "compaction", so they are not mistaken for a user follow-up message.
Test Helper
wework/e2e/fixtures/wework-app.ts provides a WeworkApp Playwright helper that wraps window.__WEWORK_E2E__ in typed test operations:
const app = new WeworkApp(page);
await app.goto("/");
await app.waitForTestId("login-form");
await app.navigate("/apps");
const route = await app.route();
New E2E tests should prefer this helper and data-testid locators instead of unstable CSS selectors or visible copy.
CI Guidance
The Backend Rust E2E image contains both wegent-backend-rs and attachment-s3-e2e. Its producer, desktop consumers, and warmup use the shared v2 image naming rule in resolve-backend-rs-e2e-runtime.sh. Their identical hash inputs include the Rust manifest, lockfile, sources, examples, and .github/runtime-images/backend-rs-e2e.Dockerfile. Recipe changes generate a new tag; old v1 images containing only the API server cannot be reused. Cache hits must still restore the complete payload. CI invokes runtime handoff unit tests that verify matching build, publication, restoration, and upload paths instead of skipping missing files or hiding cache defects behind a fallback.
The platform E2E base image, docker/wework-e2e/browser.Dockerfile, preinstalls the Docker CLI through docker.io and checks docker --version during its build. Shards use that CLI when preparing the remote workspace Executor without running apt update or installation. The Dockerfile content hash determines the image tag, so CI builds a new image after changes and reuses it in subsequent runs.
Local list bootstrap reads and manual refreshes share one revision. A superseded bootstrap read must exit before both publishing its list and starting cloud synchronization, preventing stale startup work from restarting synchronization and clearing a selected local project. The original task-discovery E2E keeps its execution-mode and local execution assertions unchanged.
Cloud Issue change notifications must invalidate an in-flight older board snapshot and start a read after the notification. Use the shared controller’s loadProjectSnapshot(projectId, { invalidate: true }) to reuse its mutation-generation protection. This prevents a reassignment notification from sharing an older read triggered by unassignment. Ordinary duplicate reads remain coalesced. Frontend CI runs the controller concurrency regression; the original human Issue E2E retains its 10-second UI wait.
Platform E2E services must start etcd and MinIO before Milvus. The GitHub runner pulls and starts containers sequentially in configuration order. Placing MinIO after Milvus can leave its DNS name unavailable during Milvus initialization while later images are being pulled. Health checks run after all containers have started and cannot repair this startup ordering.
Desktop E2E clickWhenEnabled starts the existing ten-second conversation layout diagnostic window. Milestone and failure artifacts retain conversation-diagnostics JSON with virtual ranges, message measurements, and scroll positions. These records contain no message text and do not change UI waits or business assertions. Compare them with workbench-debug.json when a completed message is missing from the rendered conversation.
On Windows, a worktree's .git file can have the hidden attribute, causing Node's default writeFile mode to return EPERM. The archived-cleanup fixture opens the existing file with r+, explicitly truncates and writes it, and closes the handle in finally. Corruption, restoration, and failure cleanup share this implementation.
For consecutive sends, compare request counts with composer diagnostics: a previous send resolving asynchronously must not clear a subsequently written draft. The pane uses an input-write version to constrain submission cleanup. Once the composer clears or the user writes the next draft, the old submission no longer owns that draft. Unit tests interleaving asynchronous acceptance and new draft writes must cover both ordinary messages and messages with code comments.
The desktop control channel registers after model fixtures finish seeding, retaining diagnostic access if seeding fails. Workbench Home migration probes use ensureLocalModelCatalogReady to await model-catalog synchronization and its app-server restart before accessing the migrated Home with native Codex. Executor startup alone does not establish catalog readiness. A failed native probe retains native-migration-turn-failure.json with the actual Home, database integrity, and thread history mode. Check initialization warnings first: a log-database open failure can surface as a list_turns error without losing thread history.
Opening project settings waits for and clicks the current project's edit or rename action directly. The sidebar chooses its menu identifier from the project's state owner and source; a snapshot taken before the menu renders cannot determine that action. Cloud project save, restart, and restored-settings assertions remain unchanged.
During local macOS verification of cloud project state files, a running ChatGPT or Codex desktop application activates the existing state-write guard. A runner whose HOME and Codex Home are both inside its isolated test-result directory can use WEGENT_FORCE_CODEX_GLOBAL_STATE_OPLOG_FLUSH=1 to verify that state reaches disk.
CI can run Wework E2E as a separate job:
pnpm install --frozen-lockfile
pnpm --filter wework exec playwright install chromium
pnpm --filter wework e2e
Desktop task-flow E2E requires a Linux runner with a graphical session, for example:
pnpm --filter wework prepare:codex
xvfb-run -a pnpm --filter wework e2e:desktop:plugins
xvfb-run -a pnpm --filter wework e2e:desktop
xvfb-run -a pnpm --filter wework e2e:desktop:cloud
GitHub Actions runs the plugins, core, and cloud Linux desktop scenarios as a parallel matrix. Each scenario receives an isolated runner, HOME, Executor Home, ports, and diagnostic artifact. This preserves the existing real Electron, Executor, and Codex verification semantics while removing the serial wait between the three scenarios. Matrix fail-fast is disabled so the remaining scenarios can finish and upload diagnostics when one scenario fails. The three scenarios reuse one prebuilt Electron artifact. Scenario-specific values such as Codex Home initialization, cloud endpoints, and test credentials are injected through the desktop E2E runtime config when Electron starts rather than compiled into the application binary. Plugins, core, and cloud can therefore share the application build while retaining isolated runtime directories and process state.
The Linux desktop scenarios cache the downloaded .deb files for Electron system
dependencies in the runner user's home directory. Cache keys rotate weekly and
are scoped by operating system and CPU architecture. Every run still executes
apt-get update; an older cache is only a restore source, and missing or
updated packages are downloaded from the Ubuntu repositories. Installation
uses --no-install-recommends plus repository retries and timeouts to reduce
whole-job timeouts caused by transient hosted-runner mirror degradation.
The memory gate depends on macOS Electron process association and physical-footprint sampling, so run it separately on a macOS runner:
pnpm --filter wework prepare:codex
pnpm --filter wework e2e:desktop:memory
The repository includes a basic workflow at .github/workflows/wework-e2e.yml. It runs when Wework, packages/chat-core, the pnpm lockfile, or the workflow itself changes.
Linux and Windows Desktop Core E2E consume the same shard matrix produced by
the classifier; a complete classification runs every declared Core shard on both
platforms. The Windows job builds native WeWork.exe,
wegent-executor.exe, and codex.exe artifacts on windows-latest before
running checkpoints. A cross-platform package or a single Windows smoke test is
not equivalent coverage.
Regular draft PRs do not run browser or desktop E2E. Merge queue runs the
complete browser plus Linux and Windows Desktop Core suites before a commit
enters main; main does not repeat checks that already passed for the merge
group. The macOS memory gate runs only for non-draft PRs carrying ci:all.
Regular PRs, scheduled runs, and manual runs do not execute memory E2E. Applying ci:all runs browser, Linux desktop, Windows
desktop, and macOS memory E2E even when the PR's changed paths would not
normally select Wework E2E. The workflow also runs a regression excluding memory E2E every
day at 04:00 UTC. Keeping ci:all on a PR also runs all Windows Core shards
on subsequent pushes. Windows builds, shards, and the summary share one
scheduling condition; skipped required jobs fail the gate. The macOS memory
check invokes the Node checkpoint runner directly, including on build cache hits.
On macOS, pre-push checks use caffeinate -is -w to remain awake while running;
the assertion ends automatically with the hook, avoiding sleep-induced test timeouts.
Authenticated flows should create users and data through backend APIs before the test, then use real login or a real token injection. Do not mock backend HTTP responses in Playwright.
Readiness for Local Project Scenarios
During installation reconciliation, plugin install/update primary actions and update confirmation reflect the same pending state as their handlers. Healthy installed plugins without updates remain available for immediate use. plugin-runtime-path-recovery retains its update confirmation, real invocation, uninstall, and restart assertions.
voice-input uses clickWhenEnabled for New Task on initial entry and after restart. Control-channel and telemetry-dialog readiness do not establish sidebar readiness. Preserve the shared 10-second action budget and all first-use setup, dialog dismissal, and post-restart assertions.
xterm updates all accessible row contents before batching transform resets, width measurements, and transform writes. This avoids per-row forced layouts. The dependency patch covers source, CommonJS, and ESM; a regression uses real xterm to verify all row contents are updated before the first measurement. background-terminals retains native paging, history, and accessibility assertions, with CPU profiles available to inspect layout hotspots.
Transcript conversion updates runtimeStatus together with the current message's status; a completed user message must not leave a stale status that overrides its streaming assistant. Smart app verification logs stage start, completion, and duration without environment variables or credentials. Locate stalled stages from these logs instead of extending E2E waits.
Smart app verification sets NPM_CONFIG_WORKSPACE_DIR to the project itself and disables implicit dependency installation before scripts. Typechecking, tests, builds, and runtime probes still execute. Verification must not read or install the containing host workspace. Real pnpm regressions cover a conflicting host package-manager version and prevent creation of its lockfile.
Desktop artifacts restore through actions/download-artifact@v6, including the upstream fix that rejects the pending promise on stream timeout instead of exiting successfully with a partial directory. See the artifact SDK timeout fix. Windows network-failure diagnostics retain connection states, dynamic/excluded port ranges, socket-exhaustion events, and memory-pool counters without process command lines or credentials.
Stand-in completion can arrive before the runtime unread flag. A new completion after observation starts must remain unread, and delivery must consult the current read ledger rather than consume it as restored history. Persist delivery records once per batch before sending alerts. Regressions cover late unread updates, no replay after reload, and a single delivery-ledger write for 1,000 records.
createSingleRootLocalProject must wait until the new project is the workbench’s current project. Closing the creation dialog or finding an older composer is insufficient. The cloud-context and running-history scenarios also select a registered model explicitly through selectE2EModel before entering the first message. Their stalled-context, attachment, history recovery, and task-move assertions remain intact.
The running-history scenario measures its three-second responsiveness gate with the workbench snapshot, excluding the debugger protocol cost of forced GC inside performanceSnapshot. Full history, pagination, transfer, and memory assertions remain intact. Windows restart checks still require the original executor to exit and record the selected PID’s process name and start time together with the taskkill exit code and output, distinguishing a surviving executor from PID reuse without collecting process command lines or environment variables.
Windows restart gates compare process instances by PID, process name and creation time instead of checking only the old PID after launching the new app. The same PID with a different creation time identifies a new instance; a surviving original instance still fails. Failed identity queries and missing creation times also fail. Keep the original model-proxy reconstruction, conversation restoration, follow-up assertions and UI timeouts. See Windows process handles and identifiers.
Checkpoint tasks use the existing 30-second model-request budget before starting the original 10-second UI completion assertion. Model startup does not consume the rendering budget. Await submission and request observation together so an observer timeout after input failure cannot become an unhandled rejection; the original error still reaches normal diagnostics and cleanup.
After a held cloud model request times out and is cancelled, explicitly refresh the catalog by opening the real model selector, then close it so recovery can reopen it. Local model settings events refresh only the local catalog; repeatedly dispatching them cannot replace a cloud refresh. Preserve the cloud failure, recovery, model provenance and plugin assertions.
When a blank conversation submission completes, reuse the current-task identity check and consume its draft key only if the created task is still selected. A late completion from the previous request must not clear text entered after returning to a new conversation. Rejected creation still restores the original draft and attachments. Regressions cover delayed creation and delayed refresh; the original nine-task bulk completion and archive E2E remains unchanged.
Chunked history transport names immutable snapshots by their compressed-content hash. Concurrent requests reuse the same file through atomic publication without replacement, avoiding replacement of a snapshot being read on Windows. Regressions preserve the published file and its modification time, keep active readers valid, and restore the complete history from four concurrent requests. The original running-history E2E remains unchanged.
Submission diagnostics retain Enter composition suppression, configured send key, and submission guard state (content presence, model readiness, and disabled controls) in the existing bounded composer event buffer without recording draft text. Trace native input, submission decisions, and Executor requests in order; the final button state does not establish its state when a key arrived.
On a Windows socket-buffer error, the desktop runner immediately saves one port, process, memory, and TCP-event snapshot (windows-network-exhaustion-at-error.json) alongside the existing failure-time snapshot. An application-startup timeout can outlast TCP port reclamation, so its later snapshot cannot exclude allocation failure at the original error. Diagnostics neither retry requests nor replace the original failure.
Publish native editor callbacks only during React layout commit. A suspended concurrent model-readiness render must not replace the send handler of the visible composer. Regressions cover native callback identity and the actual shared submission guard: an enabled composer submits with committed model readiness and remains usable after abandoning the suspended update.
Isolated Windows desktop E2E launches disable Chromium TcpPortRandomizationWin so rapid loopback connections use normal OS port allocation. Electron 44.7.0 / Chromium 152.0.7977.130 enables this feature by default; upstream 572515601 confirms WSAENOBUFS at low connection counts on Windows Server 2025 and other affected systems. Repository logs record native ERR_NO_BUFFER_SPACE together with TCP event 4231. Merge existing scenario feature switches and deduplicate entries; retain startup, model HTTP-error, and resource assertions.
The Windows desktop E2E test step has a 40-minute budget within the 60-minute job limit, leaving time to upload failure logs and diagnostic artifacts. A previous Core shard 7 hit the job deadline; its job-log endpoint returned 404 and the run log archive omitted that shard, so evidence could not be recovered from the destroyed runner. Step timeouts remain failures and retain the existing always-run uploads.
Executor RPC completion only establishes that the Executor returned a result, not that HTTP delivery finished. DSH separately records response finish or premature close events and stream state. Desktop E2E calls use unique request IDs and distinguish response-header and response-body failures, retaining elapsed time, length and encoding metadata without parameters or bodies. The plugin development child records source events, relative paths and reload generations to distinguish missing file notifications, duplicate reloads and UI synchronization failures. Diagnostics add no retries or timeout extensions.
One Windows source write can produce multiple file notifications; time debouncing does not merge every late event during a restart. Plugin development compares file content and permission digests with the last successfully loaded version to avoid duplicate reloads. New content, deletion/recreation and new notifications after a failed reload still trigger loading. Directory and unnamed notifications retain their existing reload behavior. Original HMR generation and functional E2E assertions remain unchanged. Focused Electron test paths and -t filters route together to Electron without starting an unrestricted renderer suite.
Long-history transport buffers JSON serialization fragments before feeding gzip's fast level, reducing small writes from escaped content and compression cost for interactive reads. The regression uses the original high-entropy Unicode/emoji history fixture to verify over two orders of magnitude fewer compressor-input writes with identical JSON bytes and explicit propagation of final buffered-write failures. Existing complete restoration, chunk limits, concurrent snapshot reuse and long-path coverage remain enabled. Phase logs record packing, compression and publication duration plus compressed byte counts without bodies. Chunk size, completeness assertions and hydration deadlines are unchanged.