Merge #23962's new head into brennanb2025/launch-prompt-argv-carry

Brings #24257's merge of main (e02adc5320) through #23962. Main's refactors met this branch's
carry rule in the startup builders, so the rule now runs inside them:
- Terminal create and agent-session create plan through planExecutionHostLaunchPrompt: main's
  OpenCode one-time model preparation, then planLaunchPrompt, with the model's launch config on
  whichever plan launches. A draft keeps main's refusal of a model preference.
- The flag-prompt branch uses main's extracted builder (OpenCode run's positional message and
  prompt env) with this branch's launch-file directory grant; followupPrompt is gone there too.
- Worktree create keeps main's resolveWorktreeCreateAgentStartup, now awaiting the async builder.
- The relay strips OpenCode's startup-prompt env and probes its plugin before writing the launch
  file; the local spawn creates jcode's runtime dir before it.
- Daemon protocol v41 (launch file) keeps main's fish codex-function constant at v39.
- The new-tab launcher keeps main's structured-chat-first route with this branch's launch host.
- Main's new prompted-plan tests (Qoder, Qoder CN, DSH, OpenCode startup prompt and model)
  plan through planLaunchPrompt; this branch's new launcher tests carry main's requestId.
- The agent-session startup plan, and the work-item prompt delivery, moved into their own
  functions so the merged files stay under the line cap.
This commit is contained in:
Brennan Benson
2026-10-05 01:34:09 -07:00
3609 changed files with 189904 additions and 30020 deletions
@@ -0,0 +1,94 @@
name: Publish agent state rules
# The only publisher of agent state rules releases. Merging never publishes: a maintainer runs
# this from main, first to `next` (RC and dev builds), then promotes the identical file to
# `stable` after a soak. Every job runs on the dispatched commit, so the publish job runs exactly
# the script and rules the gate tested.
#
# Why GITHUB_TOKEN: release-policy.yml deletes an agent-state-rules-* release any other author
# publishes. Its releases start no workflow, so release-policy.yml sees them only when someone
# edits one by hand, and then accepts them as bot-authored prereleases.
on:
workflow_dispatch:
inputs:
action:
description: 'publish-next builds from main and publishes to next; promote-stable copies the next file to stable'
required: true
type: choice
options:
- publish-next
- promote-stable
bundled_only:
description: 'publish-next: tell apps to fall back to the rules they shipped with; promote-stable then carries it to stable'
required: false
type: boolean
default: false
permissions:
contents: read
concurrency:
group: agent-state-rules-publish
cancel-in-progress: false
jobs:
gate:
if: github.ref == 'refs/heads/main' && inputs.action == 'publish-next'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
- name: Run the rules gate (schema, regex safety, the bundle, every transcript replay)
shell: bash
run: |
set -euo pipefail
mapfile -t gate_files < <(git ls-files \
':(glob)src/main/runtime/agent-state-rules/**/*.test.ts' \
':(glob)src/main/runtime/readiness-census*.test.ts' \
':(glob)src/main/runtime/*transcript.test.ts' \
':(glob)src/main/runtime/*transcripts.test.ts' \
config/scripts/agent-state-rules-bundle.test.mjs)
pnpm exec vitest run --config config/vitest.config.ts "${gate_files[@]}"
publish-next:
needs: gate
runs-on: ubuntu-latest
timeout-minutes: 10
environment: agent-state-rules
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Publish to next
env:
GH_TOKEN: ${{ github.token }}
BUNDLED_ONLY: ${{ inputs.bundled_only }}
shell: bash
run: |
set -euo pipefail
flags=()
[[ "$BUNDLED_ONLY" == "true" ]] && flags+=(--bundled-only)
node config/scripts/agent-state-rules-bundle.mjs publish-next "${flags[@]}"
promote-stable:
if: github.ref == 'refs/heads/main' && inputs.action == 'promote-stable'
runs-on: ubuntu-latest
timeout-minutes: 10
environment: agent-state-rules
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Copy the next file to stable
env:
GH_TOKEN: ${{ github.token }}
run: node config/scripts/agent-state-rules-bundle.mjs promote-stable
+17 -4
View File
@@ -1,16 +1,20 @@
name: Warm shared CI caches
on:
# Cache-input pushes seed immediately; this schedule repairs eviction and image changes.
schedule:
- cron: '41 * * * *'
- cron: '41 */6 * * *'
workflow_dispatch:
push:
branches: [main]
paths:
- '.github/workflows/ci-cache-warmup.yml'
- '.github/actions/install-node-dependencies/**'
- '.github/actions/restore-pnpm-verification/**'
- '.github/actions/prepare-native-runtime/**'
- '.github/actions/prepare-git-compatibility/**'
- '.github/actions/prepare-headless-compiler/**'
- 'config/scripts/headless-detector-compiler-cache*'
- '.github/actions/prepare-linux-package-fixture/**'
- 'config/docker/headless-serve-shutdown/**'
- 'config/docker/cli-launch-contract/**'
@@ -33,13 +37,14 @@ on:
- 'src/shared/zip-extractor-command.ts'
- 'config/scripts/shared-electron-dist-cache.mjs'
- 'config/scripts/space-sharing-copy.mjs'
- 'config/patches/node-pty@1.1.0.patch'
- 'config/patches/@vscode__windows-process-tree@0.8.0.patch'
- 'config/patches/**'
- 'native/windows-registry/**'
pull_request:
paths:
- '.github/workflows/ci-cache-warmup.yml'
- '.github/actions/prepare-git-compatibility/**'
- '.github/actions/prepare-headless-compiler/**'
- 'config/scripts/headless-detector-compiler-cache*'
- '.github/actions/prepare-linux-package-fixture/**'
- 'config/docker/headless-serve-shutdown/**'
- 'config/docker/cli-launch-contract/**'
@@ -51,7 +56,8 @@ permissions:
concurrency:
group: ci-cache-warmup-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
# Scheduled retries must let an active warmer finish publishing its caches.
cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs:
warm:
@@ -70,6 +76,11 @@ jobs:
native-runtime: node
node-version: '24'
cache-electron-package: 'true'
cache-pnpm-store-lookup-only: 'true'
- uses: ./.github/actions/prepare-headless-compiler
with:
seed: 'true'
- name: Populate shared Electron archive
run: node config/scripts/install-electron-package-binary.mjs
@@ -103,6 +114,7 @@ jobs:
native-runtime: node
node-version: '24'
cache-electron-package: 'true'
cache-pnpm-store-lookup-only: 'true'
- name: Populate shared Electron archive
run: node config/scripts/install-electron-package-binary.mjs
- name: Verify native cache is usable
@@ -124,6 +136,7 @@ jobs:
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
cache-pnpm-store-lookup-only: 'true'
- name: Verify native cache is usable
run: node config/scripts/ensure-native-runtime.mjs --check-only
@@ -78,6 +78,7 @@ jobs:
production:production-gce-c30) ;;
production:production-gce-c31) ;;
production:production-gce-c32,production-gce-c33) target_region=us-central1 ;;
production:production-gce-c34) ;;
*) echo "cell-ids do not match the reviewed environment topology" >&2; exit 1 ;;
esac
echo "TARGET_REGION=${target_region}" >> "${GITHUB_ENV}"
@@ -194,7 +194,7 @@ jobs:
EXPECTED_REGION=us-central1
EXPECTED_DATABASE_POOL_MAX=
;;
c27|c28|c29|c30|c31)
c27|c28|c29|c30|c31|c34)
EXPECTED_HARD_CAP=3000
EXPECTED_REGION=asia-east2
EXPECTED_DATABASE_POOL_MAX=16
+2 -2
View File
@@ -49,7 +49,7 @@ jobs:
# v5 avoids the v6 bootstrap/shim regression when pinning pnpm 10.
uses: pnpm/action-setup@v5
with:
version: 10.24.0
version: 10.34.6
package_json_file: docs/site/package.json
run_install: false
@@ -224,7 +224,7 @@ jobs:
# v5 avoids the v6 bootstrap/shim regression when pinning pnpm 10.
uses: pnpm/action-setup@v5
with:
version: 10.24.0
version: 10.34.6
package_json_file: docs/site/package.json
run_install: false
+21 -14
View File
@@ -119,7 +119,22 @@ jobs:
ref: ${{ inputs.ref || github.ref }}
- name: Install native build tools
run: sudo apt-get update && sudo apt-get install -y build-essential python3
env:
ORCA_E2E_APT_PACKAGES: build-essential python3
run: &install_e2e_tools |
read -r -a packages <<< "$ORCA_E2E_APT_PACKAGES"
for source in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
if [ -f "$source" ]; then
sudo sed -i 's|https*://azure\.archive\.ubuntu\.com/ubuntu|https://archive.ubuntu.com/ubuntu|g' "$source"
fi
done
sudo tee /etc/apt/apt.conf.d/99-orca-e2e >/dev/null <<'APTCONF'
Acquire::http::Timeout "15";
Acquire::https::Timeout "15";
Acquire::Retries "1";
APTCONF
timeout 120 sudo apt-get update
timeout 300 sudo apt-get install -y "${packages[@]}"
- uses: ./.github/actions/install-node-dependencies
with:
@@ -178,19 +193,9 @@ jobs:
- name: Install native build and headless UI tools
# The Azure archive took 16 minutes for one font package; bound setup
# separately so a slow mirror cannot consume the shard's test budget.
run: &install_e2e_tools |
for source in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
if [ -f "$source" ]; then
sudo sed -i 's|https*://azure\.archive\.ubuntu\.com/ubuntu|https://archive.ubuntu.com/ubuntu|g' "$source"
fi
done
sudo tee /etc/apt/apt.conf.d/99-orca-e2e >/dev/null <<'APTCONF'
Acquire::http::Timeout "15";
Acquire::https::Timeout "15";
Acquire::Retries "1";
APTCONF
timeout 120 sudo apt-get update
timeout 300 sudo apt-get install -y build-essential fonts-noto-cjk openssh-client python3 ripgrep xvfb zsh openbox x11-utils
env: &e2e_tool_packages
ORCA_E2E_APT_PACKAGES: build-essential fonts-noto-cjk openssh-client python3 ripgrep xvfb zsh openbox x11-utils
run: *install_e2e_tools
- uses: ./.github/actions/install-node-dependencies
with:
@@ -281,6 +286,7 @@ jobs:
# unbounded inventory fallback; the paired fixture exercises that real boundary.
# Why openssh-client: the Docker-SSH fixture shells out to ssh/ssh-keygen, and this
# lane now receives those specs from pr.yml's SSH source mapping.
env: *e2e_tool_packages
run: *install_e2e_tools
- uses: ./.github/actions/install-node-dependencies
@@ -417,6 +423,7 @@ jobs:
ref: ${{ inputs.ref || github.ref }}
- name: Install native build and headless UI tools
env: *e2e_tool_packages
run: *install_e2e_tools
- uses: ./.github/actions/install-node-dependencies
+49
View File
@@ -0,0 +1,49 @@
name: macOS updater regression tests
on:
pull_request:
paths:
- '.github/workflows/macos-updater-tests.yml'
- 'src/main/macos-update-running-instances*'
- 'src/main/updater*'
- 'src/main/updater/**'
- 'src/main/startup/main-process-quit*'
- 'src/main/window/main-window-state-lifecycle*'
- 'src/main/window/dashboard-popout-window*'
- 'src/shared/child-process/**'
- 'src/shared/update-status-types.ts'
- 'pnpm-lock.yaml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: macos-updater-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
updater:
runs-on: macos-15
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
- name: Exercise native application registry and update shutdown
env:
ORCA_BACKGROUND_LAUNCH: '1'
run: >-
pnpm exec vitest run --config config/vitest.config.ts
src/main/macos-update-running-instances.test.ts
src/main/macos-update-running-instances.integration.test.ts
src/main/updater.mac-install.test.ts
src/main/updater.headless-serve-install.test.ts
src/main/updater-mac-quit-guard.test.ts
src/main/startup/desktop-startup-ordering.test.ts
src/main/startup/main-process-quit-update-veto.test.ts
src/main/window/main-window-state-lifecycle.test.ts
src/main/window/dashboard-popout-window.test.ts
+4 -3
View File
@@ -26,6 +26,7 @@ on:
# it or to the release workflow it guards must re-run them.
- '.github/workflows/mobile.yml'
- '.github/actions/install-node-dependencies/**'
- '.github/actions/restore-pnpm-verification/**'
- '.github/workflows/mobile-ios-release.yml'
- 'config/scripts/mobile-release-check-scope*'
- 'config/scripts/mobile-test-change-scope*'
@@ -113,18 +114,18 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
# Both compilers are read-only; finish them before starting the test workers.
# Call installed tools so pnpm's dependency refresh cannot race between checks.
- name: Typecheck
id: production-types
background: true
run: pnpm typecheck
run: node node_modules/typescript/bin/tsc --noEmit
# Why a ratchet and not the raw typecheck: mobile/tsconfig.json excludes test files, so until
# tsconfig.test.json existed nothing checked them, and at introduction 127 of the 632 had
# drifted. This fails when a test file that checks today stops checking, when a test leaves
# the program, and on @ts-nocheck; the baseline may only shrink.
- name: Typecheck tests (ratchet)
run: pnpm run check:tests-typecheck
run: node scripts/check-tests-typecheck-ratchet.mjs
- wait: production-types
+70 -59
View File
@@ -16,7 +16,10 @@ on:
- '.npmrc'
- '.pnpmfile.cjs'
- '.github/actions/install-node-dependencies/**'
- '.github/actions/restore-pnpm-verification/**'
- '.github/actions/prepare-headless-compiler/**'
- '.github/actions/prepare-native-runtime/**'
- '.github/actions/prepare-orcad-prebuilds/**'
- '.github/workflows/node-server-tests.yml'
# Relevant main pushes qualify every platform after the dependency check.
push:
@@ -34,7 +37,10 @@ on:
- '.npmrc'
- '.pnpmfile.cjs'
- '.github/actions/install-node-dependencies/**'
- '.github/actions/restore-pnpm-verification/**'
- '.github/actions/prepare-headless-compiler/**'
- '.github/actions/prepare-native-runtime/**'
- '.github/actions/prepare-orcad-prebuilds/**'
- '.github/workflows/node-server-tests.yml'
workflow_dispatch:
inputs:
@@ -72,15 +78,18 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
should_run: ${{ steps.scope.outputs.should_run }}
qualification: ${{ steps.scope.outputs.qualification }}
runners: ${{ steps.scope.outputs.runners }}
should_run: ${{ steps.scope.outputs.should_run || steps.graph.outputs.should_run }}
qualification: ${{ steps.scope.outputs.qualification || steps.graph.outputs.qualification }}
runners: ${{ steps.scope.outputs.runners || steps.graph.outputs.runners }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 2
persist-credentials: false
- uses: ./.github/actions/install-node-dependencies
- uses: actions/setup-node@v6
with:
node-version-file: package.json
package-manager-cache: false
- name: Detect headless-server build and test inputs
id: scope
shell: bash
@@ -92,7 +101,7 @@ jobs:
# Compare the entire push, including multi-commit pushes and removed files.
if git fetch --no-tags --depth=1 origin "$PUSH_BASE" &&
git diff --name-only --no-renames -z "$PUSH_BASE" HEAD > "$RUNNER_TEMP/node-server-changes"; then
node config/scripts/node-server-change-scope.mjs "$RUNNER_TEMP/node-server-changes" --full-qualification
node config/scripts/node-server-change-scope.mjs "$RUNNER_TEMP/node-server-changes" --defer-graph --full-qualification
else
echo 'should_run=true' >> "$GITHUB_OUTPUT"
fi
@@ -100,11 +109,30 @@ jobs:
fi
# Compare the tested merge with its base, retaining both sides of renames.
if git diff --name-only --no-renames -z HEAD^1 HEAD > "$RUNNER_TEMP/node-server-changes"; then
node config/scripts/node-server-change-scope.mjs "$RUNNER_TEMP/node-server-changes"
node config/scripts/node-server-change-scope.mjs "$RUNNER_TEMP/node-server-changes" --defer-graph
else
echo 'should_run=true' >> "$GITHUB_OUTPUT"
fi
- uses: ./.github/actions/prepare-headless-compiler
id: compiler
if: steps.scope.outputs.graph_required == 'true'
continue-on-error: true
- uses: ./.github/actions/install-node-dependencies
if: steps.scope.outputs.graph_required == 'true' && steps.compiler.outputs.available != 'true'
- name: Check the headless import graph
id: graph
if: steps.scope.outputs.graph_required == 'true'
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
run: |
if [ "$EVENT_NAME" = push ]; then
node config/scripts/node-server-change-scope.mjs "$RUNNER_TEMP/node-server-changes" --full-qualification
else
node config/scripts/node-server-change-scope.mjs "$RUNNER_TEMP/node-server-changes"
fi
persistence:
needs: changes
concurrency:
@@ -119,7 +147,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: ${{ fromJSON(needs.changes.outputs.runners || '["ubuntu-22.04","ubuntu-24.04-arm","macos-14","macos-15-intel","windows-2022","windows-11-arm"]') }}
os: ${{ fromJSON(needs.changes.outputs.runners || '["ubuntu-22.04","ubuntu-24.04-arm","macos-15","macos-15-intel","windows-2022","windows-11-arm"]') }}
runs-on: ${{ matrix.os }}
timeout-minutes: 20
env:
@@ -132,69 +160,44 @@ jobs:
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: ${{ runner.os == 'Windows' && 'node' || 'none' }}
- name: Resolve this Windows server prebuild cache
id: orcad-prebuild-cache-identity
if: >-
runner.os == 'Windows' && (runner.arch == 'X64' || runner.arch == 'ARM64') &&
!inputs.build_template && inputs.ref == '' &&
(github.event_name == 'pull_request' ||
(github.ref == 'refs/heads/main' && contains(fromJSON('["push","schedule","workflow_dispatch"]'), github.event_name)))
continue-on-error: true
shell: bash
run: node config/scripts/orcad-windows-prebuild-cache.mjs --fingerprint
- name: Restore the exact Windows server prebuild
id: orcad-prebuild-cache-restore
if: >-
(github.event_name == 'pull_request' || github.event_name == 'push') &&
steps.orcad-prebuild-cache-identity.outcome == 'success' &&
steps.orcad-prebuild-cache-identity.outputs.key != ''
continue-on-error: true
uses: actions/cache/restore@v5
with:
path: ${{ steps.orcad-prebuild-cache-identity.outputs.path }}
key: ${{ steps.orcad-prebuild-cache-identity.outputs.key }}
# Before setup-node 18: the scripts import the TypeScript runtime pin.
# Linux release slots come from the Ubuntu 20.04 and Alpine lanes below, where their libc
# floors live; this runner's slot only packages orcad for its own tests.
- name: Build and smoke this runner's node-pty prebuild slot under the pinned Node
shell: bash
env:
WINDOWS_PREBUILD_CACHE_HIT: ${{ steps.orcad-prebuild-cache-restore.outputs.cache-hit }}
WINDOWS_PREBUILD_CACHE_RESTORE_OUTCOME: ${{ steps.orcad-prebuild-cache-restore.outcome }}
run: |
if [ "$RUNNER_OS" = Windows ] && [ "$WINDOWS_PREBUILD_CACHE_HIT" = true ] &&
[ "$WINDOWS_PREBUILD_CACHE_RESTORE_OUTCOME" = success ] &&
node config/scripts/orcad-windows-prebuild-cache.mjs --validate; then
echo 'Using the validated Windows server prebuild'
else
if [ "$RUNNER_OS" = Windows ]; then
rm -rf -- out/orcad-prebuilds
fi
pnpm build:orcad-prebuilds
fi
pnpm build:orcad-prebuilds --require-slots "$(node config/scripts/build-orcad-prebuilds.mjs --print-slot)"
pnpm build:orcad-prebuilds --smoke
- run: pnpm build:orcad
cache-pnpm-store: ${{ runner.os != 'Windows' }}
cache-pnpm-store-lookup-only: 'true'
# Design D7 upgrade and rollback: the last Bun orcad, built from a main commit that shipped
# it, beside this checkout's Node slot; the live-terminal hand-over skips once PROTOCOL_VERSION
# moves past the Bun daemon's. Same lockfile, so its build reuses this checkout's node_modules.
# moves past the Bun daemon's. Install its pinned dependencies independently of this checkout.
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
if: runner.os == 'Linux'
with:
bun-version: 1.4.2
- name: Build the last Bun orcad for the cross-runtime tests
if: runner.os == 'Linux'
id: bun-orcad
background: true
shell: bash
env:
BUN_ORCAD_COMMIT: f4092c06d639ee13ad446261dcabc78b27a21fbc
run: |
if [ "$RUNNER_OS" != Linux ]; then exit 0; fi
git fetch --no-tags --depth=1 origin "$BUN_ORCAD_COMMIT"
git worktree add --detach "$RUNNER_TEMP/bun-orcad-source" "$BUN_ORCAD_COMMIT"
ln -s "$GITHUB_WORKSPACE/node_modules" "$RUNNER_TEMP/bun-orcad-source/node_modules"
pnpm --dir "$RUNNER_TEMP/bun-orcad-source" install --frozen-lockfile --ignore-scripts
node "$RUNNER_TEMP/bun-orcad-source/config/scripts/build-orcad-bun.mjs" --out-dir "$RUNNER_TEMP/bun-orcad"
echo "ORCA_BUN_ORCAD_SLOT=$RUNNER_TEMP/bun-orcad" >> "$GITHUB_ENV"
echo "BUN_EXECUTABLE=$(command -v bun)" >> "$GITHUB_ENV"
echo "slot=$RUNNER_TEMP/bun-orcad" >> "$GITHUB_OUTPUT"
echo "executable=$(command -v bun)" >> "$GITHUB_OUTPUT"
# Linux release slots come from the floor and Alpine lanes; this slot serves local tests.
- uses: ./.github/actions/prepare-orcad-prebuilds
id: orcad-prebuild
with:
resolve-windows-cache: >-
${{ !inputs.build_template && inputs.ref == '' &&
(github.event_name == 'pull_request' ||
(github.ref == 'refs/heads/main' && contains(fromJSON('["push","schedule","workflow_dispatch"]'), github.event_name))) }}
restore-windows-cache: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
- run: pnpm build:orcad
- wait: bun-orcad
- run: pnpm test:node-server --artifact ${{ runner.os == 'Linux' && '--cross-runtime' || '' }}
env:
ORCA_BUN_ORCAD_SLOT: ${{ steps.bun-orcad.outputs.slot }}
BUN_EXECUTABLE: ${{ steps.bun-orcad.outputs.executable }}
# Only a Windows runner compiles it; arm64 cross-compiles here, as release-cut does for the relay.
# Before the Node 18 check below: the build script imports TypeScript, which Node 18 cannot load.
- name: Build the Windows process-table addons for the desktop template
@@ -229,13 +232,13 @@ jobs:
success() && runner.os == 'Windows' && (runner.arch == 'X64' || runner.arch == 'ARM64') &&
!inputs.build_template && inputs.ref == '' && github.ref == 'refs/heads/main' &&
contains(fromJSON('["push","schedule","workflow_dispatch"]'), github.event_name) &&
steps.orcad-prebuild-cache-identity.outcome == 'success' &&
steps.orcad-prebuild-cache-identity.outputs.key != ''
steps.orcad-prebuild.outputs.cache-identity-outcome == 'success' &&
steps.orcad-prebuild.outputs.cache-key != ''
continue-on-error: true
uses: actions/cache/save@v5
with:
path: ${{ steps.orcad-prebuild-cache-identity.outputs.path }}
key: ${{ steps.orcad-prebuild-cache-identity.outputs.key }}
path: ${{ steps.orcad-prebuild.outputs.cache-path }}
key: ${{ steps.orcad-prebuild.outputs.cache-key }}
# Linux release slots come from the floor and Alpine lanes; these runners own the rest.
- name: Keep this runner's qualified slot for the desktop template
if: inputs.build_template && runner.os != 'Linux'
@@ -394,11 +397,19 @@ jobs:
with:
ref: ${{ inputs.ref }}
persist-credentials: false
- uses: ./.github/actions/restore-pnpm-verification
id: pnpm-verification
with:
container-toolchain: 'true'
- name: Verify native Alpine artifact and persistence
env:
VERIFICATION_CACHE_PATH: ${{ steps.pnpm-verification.outputs.path }}
run: |
touch "$VERIFICATION_CACHE_PATH"
# Multi-arch index digest of the tag; re-resolve it whenever NODE_RUNTIME_PIN moves.
docker run --rm --init -i \
-e ORCA_BACKGROUND_LAUNCH=1 \
-v "$VERIFICATION_CACHE_PATH:/root/.cache/pnpm/lockfile-verified.jsonl" \
-v "$GITHUB_WORKSPACE:/work" -w /work \
node:24.21.0-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401c70e0e762c5b53302f905ec1c1 sh -s <<'NODE_SERVER_QUALIFICATION'
set -eu
+149 -62
View File
@@ -154,15 +154,17 @@ jobs:
echo "No specs requiring the reusable E2E workflow"
fi
static_analysis:
name: static analysis
preflight:
name: static analysis and typecheck
needs: [code_paths]
if: needs.code_paths.outputs.static_analysis == 'true'
if: needs.code_paths.outputs.static_analysis == 'true' || needs.code_paths.outputs.typecheck == 'true'
# Why ARM: measured 128s against 172s on ubuntu-latest, with every compute step faster --
# type-aware 24s->15s, anti-slop 28->19s, localization extraction 67->46s, the orcad smoke
# 39->14s. Both lint engines ship linux-arm64 and the Bun target follows process.arch, so
# the whole toolchain resolves. Free for public repositories, same as the typecheck job.
# the whole toolchain resolves. Free for public repositories.
runs-on: ubuntu-24.04-arm
outputs:
shards: ${{ steps.unit-plan.outputs.shards }}
steps:
- name: Checkout
@@ -197,21 +199,35 @@ jobs:
# Keep each check in its own log while sharing this runner.
- name: Lint
if: '!cancelled()'
id: root-lint
background: true
run: pnpm exec oxlint --format github
env:
PREFLIGHT_PHASE_SELECTED: ${{ needs.code_paths.outputs.static_analysis == 'true' }}
PREFLIGHT_PRIOR_SUCCESS: ${{ job.status == 'success' }}
run: |
if [ "$PREFLIGHT_PHASE_SELECTED" != true ] || [ "$PREFLIGHT_PRIOR_SUCCESS" != true ]; then exit 0; fi
pnpm exec oxlint --format github
- name: Reject low-evidence patterns
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run audit:anti-slop
- wait: root-lint
- name: Enforce focused code-quality plugins
if: '!cancelled()'
id: native-code-quality
background: true
run: pnpm run audit:code-quality:native
env:
PREFLIGHT_PHASE_SELECTED: ${{ needs.code_paths.outputs.static_analysis == 'true' }}
PREFLIGHT_PRIOR_SUCCESS: ${{ job.status == 'success' }}
run: |
if [ "$PREFLIGHT_PHASE_SELECTED" != true ] || [ "$PREFLIGHT_PRIOR_SUCCESS" != true ]; then exit 0; fi
pnpm run audit:code-quality:native
- name: Enforce type-aware code-quality baseline
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run audit:code-quality:type-aware
# Mobile installation changes import resolution for the native cycle check.
@@ -221,28 +237,39 @@ jobs:
# resolves types from mobile/node_modules. Without the install every mobile type
# degrades to an `error` type — reported as phantom findings against the changed lines.
- uses: ./.github/actions/install-mobile-dependencies
if: needs.code_paths.outputs.mobile_dependencies == 'true'
if: needs.code_paths.outputs.static_analysis == 'true' && needs.code_paths.outputs.mobile_dependencies == 'true'
- name: Enforce changed-code quality
if: '!cancelled()'
id: changed-code-quality
background: true
run: pnpm run check:code-quality:changed -- "${{ github.event.pull_request.base.sha }}"
env:
PREFLIGHT_PHASE_SELECTED: ${{ needs.code_paths.outputs.static_analysis == 'true' }}
PREFLIGHT_PRIOR_SUCCESS: ${{ job.status == 'success' }}
run: |
if [ "$PREFLIGHT_PHASE_SELECTED" != true ] || [ "$PREFLIGHT_PRIOR_SUCCESS" != true ]; then exit 0; fi
pnpm run check:code-quality:changed -- "${{ github.event.pull_request.base.sha }}"
- name: Enforce React Doctor on changed lines
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:react-doctor:changed -- "${{ github.event.pull_request.base.sha }}"
- wait: changed-code-quality
- name: Check Zustand selector fan-out budget
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:zustand-selector-fanout
- name: Check reliability gate manifest
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:reliability-gates
- name: Enforce dead design-system classes
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:dead-classes
- name: Check VM runtime rollback compatibility
if: needs.code_paths.outputs.static_analysis == 'true'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
@@ -264,25 +291,33 @@ jobs:
config/scripts/ephemeral-vm-runtime-store-cross-version.test.ts
- name: Enforce max-lines ratchet
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:max-lines-ratchet
- name: Enforce ts-nocheck ratchet
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:ts-nocheck-ratchet
- name: Enforce runtime Electron-import ratchet
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:runtime-electron-ratchet
- name: Check Node runtime pin
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run check:node-runtime-pin
# Why: extraction writes sorted evidence to an isolated temporary path,
# so feature PRs need one normalized AST pass rather than a three-OS matrix.
- name: Verify localization extraction
if: '!cancelled()'
id: localization-extraction
background: true
env:
PREFLIGHT_PHASE_SELECTED: ${{ needs.code_paths.outputs.static_analysis == 'true' }}
PREFLIGHT_PRIOR_SUCCESS: ${{ job.status == 'success' }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
if [ "$PREFLIGHT_PHASE_SELECTED" != true ] || [ "$PREFLIGHT_PRIOR_SUCCESS" != true ]; then exit 0; fi
# Detection failures run the full check; renames retain the removed input path.
DIFF_BASE="$(node config/scripts/git-pull-request-diff-base.mjs "$BASE_SHA")"
if git diff --name-only --no-renames -z "$DIFF_BASE" HEAD > "$RUNNER_TEMP/localization-changes" &&
@@ -296,6 +331,7 @@ jobs:
# which is a property of the import graph. This proves the Node artifact it enables
# actually boots, pairs, creates a worktree and round-trips a real PTY.
- name: Boot orcad and round-trip a terminal
if: needs.code_paths.outputs.static_analysis == 'true'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
ORCA_BACKGROUND_LAUNCH: '1'
@@ -310,20 +346,30 @@ jobs:
fi
- name: Verify the generated RPC params catalog
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run verify:rpc-params-catalog
- name: Verify bundled skill guides
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run verify:bundled-skill-guides
- name: Verify skill freshness manifest
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run verify:skill-bundle-manifest
- name: Verify localization catalogs
if: '!cancelled()'
id: localization-catalogs
background: true
run: pnpm run verify:localization-catalogs
env:
PREFLIGHT_PHASE_SELECTED: ${{ needs.code_paths.outputs.static_analysis == 'true' }}
PREFLIGHT_PRIOR_SUCCESS: ${{ job.status == 'success' }}
run: |
if [ "$PREFLIGHT_PHASE_SELECTED" != true ] || [ "$PREFLIGHT_PRIOR_SUCCESS" != true ]; then exit 0; fi
pnpm run verify:localization-catalogs
- name: Verify localization coverage
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm run verify:localization-coverage
- wait: [localization-catalogs, localization-extraction]
@@ -334,6 +380,7 @@ jobs:
# in .d.ts to `any`, which is how #1186 shipped a broken IPC signature
# past typecheck. See .github/CONTRIBUTING.md#type-declarations-prefer-ts-over-dts.
- name: Guard against project-owned .d.ts in preload/shared
if: needs.code_paths.outputs.static_analysis == 'true'
run: |
matches=$(find src/preload src/shared -name '*.d.ts' 2>/dev/null || true)
if [ -n "$matches" ]; then
@@ -346,33 +393,19 @@ jobs:
fi
- name: Check feature wall asset budget
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm check:feature-wall-assets
- name: Verify macOS entitlements
if: needs.code_paths.outputs.static_analysis == 'true'
run: pnpm verify:macos-entitlements
typecheck:
needs: [code_paths]
if: needs.code_paths.outputs.typecheck == 'true'
# Typechecking uses no native runtime, so it can use the free public ARM runner.
runs-on: ubuntu-24.04-arm
outputs:
shards: ${{ steps.unit-plan.outputs.shards }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 2
persist-credentials: false
- uses: ./.github/actions/install-node-dependencies
# Why: every project is `composite`, so tsc already writes a .tsbuildinfo that lets
# the next run skip unchanged files. Share one cache entry across commits while the
# PR base stays stable; actions/cache keeps the first successful graph and the
# compiler still invalidates stale files from its content hashes.
- name: Cache TypeScript incremental state
if: needs.code_paths.outputs.typecheck == 'true'
uses: actions/cache@v5
with:
path: config/*.tsbuildinfo
@@ -382,17 +415,24 @@ jobs:
# Planning shares setup and stays off the compiler's critical path.
- name: Plan unit selection
if: '!cancelled()'
id: unit-plan
background: true
env:
PREFLIGHT_PHASE_SELECTED: ${{ needs.code_paths.outputs.typecheck == 'true' }}
PREFLIGHT_PRIOR_SUCCESS: ${{ job.status == 'success' }}
ORCA_UNIT_SELECTION_MODE: ${{ vars.ORCA_UNIT_SELECTION_MODE || 'shadow' }}
run: node config/scripts/ci-unit-plan.mjs
run: |
if [ "$PREFLIGHT_PHASE_SELECTED" != true ] || [ "$PREFLIGHT_PRIOR_SUCCESS" != true ]; then exit 0; fi
node config/scripts/ci-unit-plan.mjs
- run: pnpm run typecheck
if: needs.code_paths.outputs.typecheck == 'true'
- wait: unit-plan
- uses: actions/upload-artifact@v7
if: needs.code_paths.outputs.typecheck == 'true'
with:
name: unit-selection-attempt-${{ github.run_attempt }}
path: ci-shards/unit-selection.json
@@ -400,8 +440,12 @@ jobs:
git_compatibility:
name: Git compatibility
needs: [code_paths]
if: needs.code_paths.outputs.git_compatibility == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.git_compatibility == 'true' &&
needs.preflight.result == 'success'
runs-on: ubuntu-latest
steps:
@@ -415,6 +459,8 @@ jobs:
- uses: ./.github/actions/prepare-git-compatibility
- name: Verify Git binary compatibility matrix
env:
ORCA_BACKGROUND_LAUNCH: '1'
run: |
specs=(
"alpine/git:edge-2.38.1|2.38.1"
@@ -429,9 +475,13 @@ jobs:
pids=()
(
ORCA_GIT_COMPAT_BINARY="$HOME/.cache/orca-git-compat/git-2.25.5/git" \
GIT_EXEC_PATH="$HOME/.cache/orca-git-compat/git-2.25.5" \
ORCA_GIT_COMPAT_VERSION="2.25.5" \
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/git-binary-compatibility.test.ts
src/shared/git-binary-compatibility.test.ts \
src/main/git/worktree-safety-real-git.test.ts \
src/main/git/worktree-rebase-update-refs-real-git.test.ts \
src/relay/git-review-draft-binary-compatibility.test.ts
) &
pids+=("$!")
@@ -441,7 +491,10 @@ jobs:
version="${spec#*|}"
ORCA_GIT_COMPAT_IMAGE="$image" ORCA_GIT_COMPAT_VERSION="$version" \
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/git-binary-compatibility.test.ts
src/shared/git-binary-compatibility.test.ts \
src/main/git/worktree-safety-real-git.test.ts \
src/main/git/worktree-rebase-update-refs-real-git.test.ts \
src/relay/git-review-draft-binary-compatibility.test.ts
) &
pids+=("$!")
done
@@ -460,8 +513,12 @@ jobs:
# repair stops happening. Pinned because the binary is the thing expected to drift.
codex_index_heal_contract:
name: Codex index-heal contract
needs: [code_paths]
if: needs.code_paths.outputs.codex_index_heal_contract == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.codex_index_heal_contract == 'true' &&
needs.preflight.result == 'success'
# Why ARM: @openai/codex ships @openai/codex-linux-arm64.
runs-on: ubuntu-24.04-arm
env:
@@ -526,8 +583,12 @@ jobs:
xterm_patch_sync:
name: xterm patch sync
needs: [code_paths]
if: needs.code_paths.outputs.xterm_patch_sync == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.xterm_patch_sync == 'true' &&
needs.preflight.result == 'success'
# Why ARM: the patch check rebuilds 4 packages x 2 builds and byte-compares against the
# checked-in bundles. Those were generated on Linux x64 and reproduce byte-for-byte on
# darwin-arm64, so the output is neither host-arch nor host-OS dependent.
@@ -555,8 +616,12 @@ jobs:
shell_contracts:
name: shell contracts
needs: [code_paths]
if: needs.code_paths.outputs.shell_contracts == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.shell_contracts == 'true' &&
needs.preflight.result == 'success'
# Why ARM: fish 4.x is published for noble/arm64 and zsh is in the arm64 archive.
runs-on: ubuntu-24.04-arm
# Why: this job's cost is almost entirely package download, and a stalled mirror has
@@ -671,6 +736,7 @@ jobs:
src/main/pty/omp-shell-wrapper.node-pty.test.ts \
src/main/fish-xdg-data-dirs-handoff.test.ts \
src/main/shell-startup-feature-channel.test.ts \
src/main/zsh-deferred-startup-line-init.live-shell.test.ts \
src/main/terminal-history-fish-session.node-pty.test.ts \
src/main/zsh-scoped-histfile.live-shell.test.ts \
src/main/zsh-startup-hook-user-config-equivalence.live-shell.test.ts \
@@ -682,35 +748,38 @@ jobs:
src/shared/startup-shell-portability.live-shell.test.ts \
src/shared/posix-command-path-lookup.test.ts
# Static analysis saves the Node cache; typecheck publishes the plan before tests fan out.
# Preflight saves the Node cache and publishes the plan before tests fan out.
test:
needs: [code_paths, static_analysis, typecheck]
needs: [code_paths, preflight]
# Cancellation and failed prerequisites stop the expensive matrix.
if: >-
!cancelled() &&
needs.code_paths.outputs.test == 'true' &&
needs.static_analysis.result == 'success' &&
needs.typecheck.result == 'success'
needs.preflight.result == 'success'
uses: ./.github/workflows/unit-tests.yml
with:
node_versions: '["24"]'
runner: ubuntu-24.04-arm
shards: ${{ needs.typecheck.outputs.shards }}
shards: ${{ needs.preflight.outputs.shards }}
# Why a sibling and not part of the test workflow: it is advisory, so it must not delay the
# gate. Inside unit-tests.yml a caller's `needs: test` waited for it, holding verify ~36s
# after the last shard. Deliberately absent from verify's needs for the same reason.
unit_selection_evidence:
needs: [test]
if: ${{ !cancelled() && needs.test.result == 'success' }}
if: ${{ !cancelled() && (needs.test.result == 'success' || needs.test.result == 'failure') }}
uses: ./.github/workflows/unit-selection-evidence.yml
# Why a separate job: the test needs a real Chrome, and the sharded `test` matrix
# would pay for it on every shard to run one file in whichever shard it landed in.
orcad_browser:
name: orcad browser provider
needs: [code_paths]
if: needs.code_paths.outputs.orcad_browser == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.orcad_browser == 'true' &&
needs.preflight.result == 'success'
runs-on: ubuntu-latest
steps:
@@ -762,7 +831,7 @@ jobs:
# in this job loads node-pty.
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
native-runtime: none
cache-dependency-path: |
pnpm-lock.yaml
mobile/pnpm-lock.yaml
@@ -823,8 +892,12 @@ jobs:
cross-version-wire:
name: cross-version wire compatibility
needs: [code_paths]
if: needs.code_paths.outputs.cross-version-wire == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.cross-version-wire == 'true' &&
needs.preflight.result == 'success'
# Why ARM: source-only: tagged checkout plus in-process vitest, no docker or browser.
runs-on: ubuntu-24.04-arm
@@ -867,8 +940,12 @@ jobs:
managed_hook_node18:
name: managed hooks on Node 18
needs: [code_paths]
if: needs.code_paths.outputs.managed_hook_node18 == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.managed_hook_node18 == 'true' &&
needs.preflight.result == 'success'
# Why ARM: Node 18 publishes linux-arm64; the per-platform runtime files are read as data.
runs-on: ubuntu-24.04-arm
@@ -893,7 +970,7 @@ jobs:
package:
name: package
needs: [code_paths, static_analysis, typecheck]
needs: [code_paths, preflight]
if: needs.code_paths.outputs.package == 'true'
runs-on: ubuntu-latest
# Let the serial Docker gates reach their own deadlines and report cleanup failures.
@@ -1050,7 +1127,7 @@ jobs:
package_windows:
name: package (windows)
needs: [code_paths, static_analysis, typecheck]
needs: [code_paths, preflight]
if: needs.code_paths.outputs.package_windows == 'true'
runs-on: windows-2022
timeout-minutes: 30
@@ -1129,6 +1206,7 @@ jobs:
src/shared/child-process/windows-cmd-shim-resolution.test.ts
src/shared/child-process/windows-cmd-shim-resolution.win32.test.ts
src/main/agent-hooks/windows-hook-payload-delivery.test.ts
src/main/jcode/hook-gate-script.test.ts
src/main/agent-hooks/windows-direct-cmd-hook-command.test.ts
src/main/codex/windows-hook-command.test.ts
src/main/codex/windows-hook-upgrade.test.ts
@@ -1159,6 +1237,8 @@ jobs:
src/main/runtime/unreadable-secret-store-preservation.win32.test.ts
src/main/ipc/pty-codex-account-attribution.test.ts
src/main/ipc/pty-spawn-env-codex-resume-provenance.test.ts
src/main/ipc/preflight-provider-command-selection.test.ts
src/main/ipc/preflight-runnable-local-cli.test.ts
src/relay/windows-port-scan.win32.test.ts
src/main/ssh/ssh-relay-upload-stage-windows-identity.test.ts
src/main/ssh/remote-node-runtime-store-windows.test.ts
@@ -1212,8 +1292,19 @@ jobs:
e2e:
name: e2e
needs: code_paths
if: needs.code_paths.outputs.e2e_should_run == 'true'
needs: [code_paths, preflight]
if: >-
!cancelled() &&
needs.code_paths.result == 'success' &&
needs.code_paths.outputs.e2e_should_run == 'true' &&
(
needs.preflight.result == 'success' ||
(
needs.preflight.result == 'skipped' &&
needs.code_paths.outputs.static_analysis == 'false' &&
needs.code_paths.outputs.typecheck == 'false'
)
)
# Why: reusable e2e.yml only checkouts, builds, and uploads artifacts.
permissions:
contents: read
@@ -1256,8 +1347,7 @@ jobs:
if: ${{ !cancelled() }}
needs:
- code_paths
- static_analysis
- typecheck
- preflight
- git_compatibility
- codex_index_heal_contract
- xterm_patch_sync
@@ -1286,10 +1376,8 @@ jobs:
env:
CODE_PATHS: ${{ needs.code_paths.result }}
SHOULD_RUN: ${{ needs.code_paths.outputs.should_run }}
STATIC_ANALYSIS: ${{ needs.static_analysis.result }}
STATIC_ANALYSIS_SHOULD_RUN: ${{ needs.code_paths.outputs.static_analysis }}
TYPECHECK: ${{ needs.typecheck.result }}
TYPECHECK_SHOULD_RUN: ${{ needs.code_paths.outputs.typecheck }}
PREFLIGHT: ${{ needs.preflight.result }}
PREFLIGHT_SHOULD_RUN: ${{ needs.code_paths.outputs.static_analysis == 'true' || needs.code_paths.outputs.typecheck == 'true' }}
GIT_COMPATIBILITY: ${{ needs.git_compatibility.result }}
GIT_COMPATIBILITY_SHOULD_RUN: ${{ needs.code_paths.outputs.git_compatibility }}
CODEX_INDEX_HEAL_CONTRACT: ${{ needs.codex_index_heal_contract.result }}
@@ -1335,8 +1423,7 @@ jobs:
fi
}
# Require success when the PR has code-relevant changes
check_job static_analysis "$STATIC_ANALYSIS" "$STATIC_ANALYSIS_SHOULD_RUN"
check_job typecheck "$TYPECHECK" "$TYPECHECK_SHOULD_RUN"
check_job preflight "$PREFLIGHT" "$PREFLIGHT_SHOULD_RUN"
check_job git_compatibility "$GIT_COMPATIBILITY" "$GIT_COMPATIBILITY_SHOULD_RUN"
check_job codex_index_heal_contract "$CODEX_INDEX_HEAL_CONTRACT" "$CODEX_INDEX_HEAL_CONTRACT_SHOULD_RUN"
check_job xterm_patch_sync "$XTERM_PATCH_SYNC" "$XTERM_PATCH_SYNC_SHOULD_RUN"
+54 -7
View File
@@ -79,6 +79,7 @@ jobs:
source_ref: ${{ steps.resolve.outputs.ref }}
source_sha: ${{ steps.resolve.outputs.sha }}
source_short_sha: ${{ steps.resolve.outputs.short_sha }}
ships_orcad_template: ${{ steps.orcad-template-support.outputs.ships }}
steps:
# Why inlined (not m-s-abeer/update-gha-summary-with-workflow-inputs):
# this job runs with contents:write and secret scope, so avoid executing
@@ -783,6 +784,21 @@ jobs:
git push origin "$TAG"
fi
# Why: a patch cut from a base older than #24155 has no orcad template to build or ship.
- name: Detect whether the tag ships the orcad template
id: orcad-template-support
if: steps.tag.outputs.tag != '' || steps.version.outputs.recovered_tag != ''
env:
TAG: ${{ steps.tag.outputs.tag || steps.version.outputs.recovered_tag }}
run: |
set -euo pipefail
if git cat-file -e "refs/tags/${TAG}^{commit}:config/scripts/packaged-orcad-template.cjs" 2>/dev/null; then
echo "ships=true" >>"$GITHUB_OUTPUT"
else
echo "ships=false" >>"$GITHUB_OUTPUT"
echo "::notice::$TAG predates the orcad template; skipping its build and download."
fi
- name: Release E2E signal summary
if: always()
run: |
@@ -1154,7 +1170,7 @@ jobs:
# signing quota, so it runs beside the release gates instead of behind them.
orcad-template:
needs: cut
if: needs.cut.outputs.should_release == 'true'
if: needs.cut.outputs.should_release == 'true' && needs.cut.outputs.ships_orcad_template == 'true'
permissions:
contents: read
uses: ./.github/workflows/node-server-tests.yml
@@ -1203,7 +1219,16 @@ jobs:
- orcad-template
- release-preflight
- relay-windows-process-tree
if: needs.cut.outputs.should_release == 'true'
# Why not the implicit success(): a tag without the orcad template skips that job on purpose.
if: >-
!cancelled() &&
needs.cut.result == 'success' &&
needs.cut.outputs.should_release == 'true' &&
needs.create-release.result == 'success' &&
needs.release-preflight.result == 'success' &&
needs.relay-windows-process-tree.result == 'success' &&
(needs.orcad-template.result == 'success' ||
(needs.orcad-template.result == 'skipped' && needs.cut.outputs.ships_orcad_template == 'false'))
env:
# beforePack and afterPack fail the package when the template is absent.
ORCA_REQUIRE_ORCAD_TEMPLATE: '1'
@@ -1463,6 +1488,7 @@ jobs:
# After the app build so nothing that cleans out/ can drop it; electron-builder ships it.
- name: Download the orcad deployment template
if: needs.cut.outputs.ships_orcad_template == 'true'
uses: actions/download-artifact@v8
with:
name: orcad-template
@@ -1806,7 +1832,7 @@ jobs:
# each file against the template manifest, so it must record the signed bytes.
- name: Reseal the orcad template over its signed binaries
id: reseal-orcad-template
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.restore-signed-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.restore-signed-inner.outcome == 'success' && needs.cut.outputs.ships_orcad_template == 'true'
run: node config/scripts/packaged-orcad-template.cjs --reseal-signed dist/win-unpacked inner-signing-list.txt
# The uninstaller must return signed before rebuilding the installer.
@@ -2306,7 +2332,16 @@ jobs:
- release-preflight
# release-mac-build.yml downloads the relay addons from this run.
- relay-windows-process-tree
if: needs.cut.outputs.should_release == 'true'
# Why not the implicit success(): a tag without the orcad template skips that job on purpose.
if: >-
!cancelled() &&
needs.cut.result == 'success' &&
needs.cut.outputs.should_release == 'true' &&
needs.create-release.result == 'success' &&
needs.release-preflight.result == 'success' &&
needs.relay-windows-process-tree.result == 'success' &&
(needs.orcad-template.result == 'success' ||
(needs.orcad-template.result == 'skipped' && needs.cut.outputs.ships_orcad_template == 'false'))
# Why: SignPath requires every job in this signing workflow to be
# GitHub-hosted. The actual mac build runs in release-mac-build.yml so
# Blacksmith stays outside Windows artifact provenance.
@@ -2340,6 +2375,15 @@ jobs:
- skill-sharing-linux-floor-release-gate
- skill-sharing-release-gate
- terminal-rendering-golden
# Why explicit: a skipped orcad-template (tags that predate it) would skip every job after it.
if: >-
!cancelled() &&
needs.cut.result == 'success' &&
needs.build.result == 'success' &&
needs.build-mac.result == 'success' &&
needs.skill-sharing-linux-floor-release-gate.result == 'success' &&
needs.skill-sharing-release-gate.result == 'success' &&
needs.terminal-rendering-golden.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: write
@@ -2408,7 +2452,8 @@ jobs:
needs:
- cut
- publish-release
if: ${{ needs.cut.outputs.tag != '' }}
# Why explicit: a skipped orcad-template (tags that predate it) would skip every job after it.
if: ${{ !cancelled() && needs.publish-release.result == 'success' && needs.cut.outputs.tag != '' }}
runs-on: ubuntu-latest
permissions:
actions: write
@@ -2437,7 +2482,8 @@ jobs:
# A release created with GITHUB_TOKEN does not reliably emit a release
# event to other workflows. Dispatch the trusted default-branch workflow;
# it validates and checks out the released tag before deploying.
if: ${{ needs.cut.outputs.tag != '' }}
# Why explicit: a skipped orcad-template (tags that predate it) would skip every job after it.
if: ${{ !cancelled() && needs.publish-release.result == 'success' && needs.cut.outputs.tag != '' }}
runs-on: ubuntu-latest
permissions:
actions: write
@@ -2481,7 +2527,8 @@ jobs:
needs:
- cut
- publish-release
if: ${{ needs.cut.outputs.tag != '' && startsWith(needs.cut.outputs.tag, 'v') }}
# Why explicit: a skipped orcad-template (tags that predate it) would skip every job after it.
if: ${{ !cancelled() && needs.publish-release.result == 'success' && needs.cut.outputs.tag != '' && startsWith(needs.cut.outputs.tag, 'v') }}
uses: ./.github/workflows/homebrew-bump.yml
with:
tag: ${{ needs.cut.outputs.tag }}
+2
View File
@@ -158,6 +158,8 @@ jobs:
# Design D2: the parent release-cut run merged it from every node-server lane at this tag.
- name: Download the orcad deployment template from the release run
# Why: release-cut skips the template for a tag that predates it.
if: hashFiles('config/scripts/packaged-orcad-template.cjs') != ''
uses: actions/download-artifact@v8
with:
name: orcad-template
+3 -1
View File
@@ -22,7 +22,9 @@ jobs:
# Why: release events run this file from the tagged commit, so load the module from the same commit.
- uses: actions/checkout@v6
with:
sparse-checkout: config/scripts/release-policy.mjs
sparse-checkout: |
config/scripts/release-policy.mjs
config/scripts/release-tag-patterns.mjs
sparse-checkout-cone-mode: false
persist-credentials: false
- name: Enforce release policy
+21 -2
View File
@@ -27,6 +27,7 @@ on:
- '!src/**/*.test.ts'
- 'src/main/ssh/ssh-relay-hostile-hosts.docker.test.ts'
- '.github/workflows/ssh-hostile-hosts.yml'
- '.github/actions/restore-pnpm-verification/**'
workflow_dispatch:
inputs:
cells:
@@ -56,7 +57,17 @@ jobs:
PYTHON: /opt/python/cp312-cp312/bin/python3
steps:
- name: Install glibc 2.28 prerequisites
run: dnf install -y git procps-ng unzip which xz
run: |
missing_tool=false
for tool in git ps unzip which xz; do
if ! command -v "$tool" >/dev/null 2>&1; then
missing_tool=true
fi
done
if [ "$missing_tool" = true ]; then
# The image's source-built Git needs no RPM; missing tools come from AlmaLinux.
dnf --disablerepo='epel*' install -y git procps-ng unzip which xz
fi
- uses: actions/checkout@v6
with:
persist-credentials: false
@@ -89,11 +100,19 @@ jobs:
with:
name: hostile-hosts-glibc-slot
path: out/orcad-prebuilds
- uses: ./.github/actions/restore-pnpm-verification
id: pnpm-verification
with:
container-toolchain: 'true'
- name: Build and smoke the linux-x64-musl slot on Alpine
env:
VERIFICATION_CACHE_PATH: ${{ steps.pnpm-verification.outputs.path }}
run: |
touch "$VERIFICATION_CACHE_PATH"
# Same digest as the headless-server musl lane; re-resolve it whenever NODE_RUNTIME_PIN moves.
docker run --rm --init -i \
-e ORCA_BACKGROUND_LAUNCH=1 \
-v "$VERIFICATION_CACHE_PATH:/root/.cache/pnpm/lockfile-verified.jsonl" \
-v "$GITHUB_WORKSPACE:/work" -w /work \
node:24.21.0-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401c70e0e762c5b53302f905ec1c1 sh -s <<'MUSL_SLOT'
set -eu
@@ -198,7 +217,7 @@ jobs:
fail-fast: false
matrix:
include:
- os: macos-14
- os: macos-15
target: darwin-arm64
cell: macos-arm64-local-sshd
- os: macos-15-intel
+51 -8
View File
@@ -28,12 +28,16 @@ on:
- 'config/patches/@vscode__windows-process-tree*'
- 'config/scripts/build-windows-process-tree-relay-addon.mjs'
- 'config/scripts/relay-windows-process-tree-staging.mjs'
- 'config/scripts/relay-windows-process-tree-prepared-addon*.mjs'
- 'config/scripts/windows-process-tree-gyp-rebuild.mjs'
- 'src/shared/relay-windows-breakaway-launch.ts'
- '!src/**/*.test.ts'
- 'src/main/ssh/ssh-relay-windows-host-lane.test.ts'
- 'config/ci/windows-ssh-provider/**'
- '.github/workflows/ssh-windows-hosts.yml'
- '.github/actions/prepare-orcad-prebuilds/**'
- '.github/actions/restore-pnpm-verification/**'
- 'config/scripts/orcad-windows-prebuild-cache.mjs'
workflow_dispatch:
inputs:
cells:
@@ -80,9 +84,17 @@ jobs:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
# Keep complete server/test trees; missing future imports fail the unchanged builds.
sparse-checkout: |
.github
config
native
resources
tests
src/main
src/shared
src/relay
src/types
- name: Self-test the provisioning scripts before touching the machine
shell: pwsh
run: |
@@ -92,20 +104,49 @@ jobs:
if($errors.Count){throw "PowerShell parse failed: $($file.FullName)"}
}
& config/ci/windows-ssh-provider/preview-ssh/test-preview-diagnostics.ps1
# ARM inbox servicing can finish while the independent Node artifacts are prepared.
- name: Prepare the Windows inbox SSH capability
id: inbox-capability
background: true
shell: pwsh
run: |
if('${{ matrix.server }}' -eq 'inbox' -and '${{ matrix.arch }}' -eq 'arm64'){
$receipts=Join-Path $pwd '.build/ssh-windows-host-receipts'
New-Item -ItemType Directory -Force -Path $receipts | Out-Null
. config/ci/windows-ssh-provider/preview-ssh/windows-ssh-capability.ps1
Initialize-WindowsInboxSshCapability -Arch '${{ matrix.arch }}' -Receipt (Join-Path $receipts 'inbox-capability-preparation.json')
}
- uses: ./.github/actions/install-node-dependencies
id: dependencies
with:
native-runtime: node
cache-pnpm-store: 'false'
# The deploy materializes rung A from this template; only this runner's slot exists here.
# The process-tree addon carries the launcher that starts the relay outside sshd's job; the
# orcad slot and the relay both stage it, and a standard-user host has no other launch route.
- name: Build this runner's orcad slot, the win32 template and the relay
- name: Build this runner's Windows process-table addon
shell: bash
env:
REUSE_PREPARED_RUNTIME: ${{ github.event_name == 'pull_request' && steps.dependencies.outputs.native-cache-hit == 'true' }}
run: |
reuse_args=()
if [ "$REUSE_PREPARED_RUNTIME" = true ]; then
reuse_args+=(--reuse-prepared-runtime)
fi
node config/scripts/build-windows-process-tree-relay-addon.mjs --arch=${{ matrix.arch }} "${reuse_args[@]}"
- uses: ./.github/actions/prepare-orcad-prebuilds
with:
resolve-windows-cache: ${{ github.event_name == 'pull_request' }}
restore-windows-cache: ${{ github.event_name == 'pull_request' }}
- name: Build the win32 template and the relay
shell: bash
env:
ORCA_REQUIRE_RELAY_NATIVE_ADDONS: ${{ matrix.arch }}
run: |
node config/scripts/build-windows-process-tree-relay-addon.mjs --arch=${{ matrix.arch }}
pnpm build:orcad-prebuilds
pnpm build:orcad-prebuilds --require-slots "win32-${{ matrix.arch }}"
pnpm build:orcad-prebuilds --smoke
node config/scripts/build-orcad-template.mjs --targets "win32-${{ matrix.arch }}"
pnpm run build:relay
- wait: inbox-capability
- name: Run the Windows host cells against a private ${{ matrix.server }} sshd
shell: pwsh
timeout-minutes: 50
@@ -119,6 +160,8 @@ jobs:
$cells=@($env:CELLS -split ',' | ForEach-Object {$_.Trim()} | Where-Object {$_})
if(-not $cells.Count){$cells=@('pinned-cmd','pinned-powershell','legacy-opt-out')}
$archive=''
$preparation=''
if('${{ matrix.server }}' -eq 'inbox' -and '${{ matrix.arch }}' -eq 'arm64'){$preparation=Join-Path $receipts 'inbox-capability-preparation.json'}
if('${{ matrix.server }}' -eq 'preview'){
$archive=Join-Path $env:RUNNER_TEMP 'preview-${{ matrix.archive }}'
# The provisioning script refuses the archive unless its sha256 and every binary's match the pin.
@@ -128,7 +171,7 @@ jobs:
$callback={param($context)
& (Join-Path $tools 'invoke-pinned-relay-cells.ps1') -SourceRoot $sourceRoot -Context $context -Target 'win32-${{ matrix.arch }}' -ReceiptRoot $receipts -Cells $cells
}.GetNewClosure()
& (Join-Path $tools 'preview-ssh/prove-preview-openssh.ps1') -Archive $archive -Arch '${{ matrix.arch }}' -Server '${{ matrix.server }}' -Receipt (Join-Path $receipts 'provider-server.json') -Accounts $cells.Count -HiddenTools @('npm','npx','node-gyp','gcc','g++','cc','c++','make','cl','clang','clang++','msbuild','cmake') -HostCellProbe $callback 2>&1 | Tee-Object (Join-Path $receipts 'provision.log')
& (Join-Path $tools 'preview-ssh/prove-preview-openssh.ps1') -Archive $archive -Arch '${{ matrix.arch }}' -Server '${{ matrix.server }}' -Receipt (Join-Path $receipts 'provider-server.json') -InboxPreparationReceipt $preparation -Accounts $cells.Count -HiddenTools @('npm','npx','node-gyp','gcc','g++','cc','c++','make','cl','clang','clang++','msbuild','cmake') -HostCellProbe $callback 2>&1 | Tee-Object (Join-Path $receipts 'provision.log')
- uses: actions/upload-artifact@v7
if: always()
with:
+10
View File
@@ -5,12 +5,21 @@ on:
# change is worth a real ibus session. A pull_request trigger here would run it on every PR.
workflow_call:
workflow_dispatch:
inputs:
diagnose_wayland_input:
description: Capture passive Wayland input diagnostics while running both native lanes
required: false
type: boolean
default: false
schedule:
- cron: '30 9 * * *'
permissions:
contents: read
env:
ORCA_BACKGROUND_LAUNCH: '1'
jobs:
linux-x11:
name: Linux X11 terminal IME
@@ -98,6 +107,7 @@ jobs:
- name: Run native Wayland Hangul terminating digit
env:
SKIP_BUILD: '1'
ORCA_E2E_WAYLAND_INPUT_DIAGNOSTICS: ${{ inputs.diagnose_wayland_input && '1' || '' }}
run: node config/scripts/run-terminal-ibus-hangul-e2e.mjs --nested-wayland
- name: Upload Wayland terminal IME evidence
if: always()
+37 -2
View File
@@ -68,25 +68,60 @@ jobs:
- name: Install native build tools and xvfb
run: sudo apt-get update && sudo apt-get install -y build-essential python3 xvfb zsh
- name: Select dependency preparation
id: install-mode
shell: bash
env:
RUNNER_KIND: ${{ runner.environment }}
JOB_CONTAINER: ${{ job.container.id }}
run: |
node <<'NODE'
const fs = require('node:fs')
const manifest = JSON.parse(fs.readFileSync('package.json', 'utf8'))
const actionPath = '.github/actions/install-node-dependencies/action.yml'
const action = fs.existsSync(actionPath) ? fs.readFileSync(actionPath, 'utf8') : ''
const inputs = action.split(/^inputs:[ \t]*\r?$/m)[1]?.split(/^\S/m)[0] ?? ''
const shared = process.env.RUNNER_KIND === 'github-hosted' && !process.env.JOB_CONTAINER &&
process.env.RUNNER_OS === 'Linux' && process.env.RUNNER_ARCH === 'X64' &&
manifest.engines?.node === '24' && typeof manifest.packageManager === 'string' &&
manifest.packageManager.split('+')[0] === 'pnpm@12.8.1' &&
manifest.scripts?.postinstall === 'node config/scripts/rebuild-native-deps.mjs' &&
/^ native-runtime:/m.test(inputs) && /^ cache-pnpm-store-lookup-only:/m.test(inputs) &&
fs.existsSync('.github/actions/prepare-native-runtime/action.yml') &&
fs.existsSync('config/scripts/ensure-native-runtime.mjs')
fs.appendFileSync(process.env.GITHUB_OUTPUT, `shared=${shared}\n`)
NODE
- name: Prepare current dependencies
if: steps.install-mode.outputs.shared == 'true'
uses: ./.github/actions/install-node-dependencies
with:
native-runtime: electron
cache-electron-package: 'true'
cache-pnpm-store-lookup-only: 'true'
- name: Setup pnpm
if: steps.install-mode.outputs.shared != 'true'
uses: pnpm/setup@v2
with:
install: false
- name: Setup Node.js
if: steps.install-mode.outputs.shared != 'true'
uses: actions/setup-node@v6
with:
node-version-file: package.json
cache: pnpm
# Why: this scheduled/manual workflow uses the same native install path as
# PR and E2E CI, which needs pnpm to bypass its bundled gyp_main.py.
# Historical refs can lack the shared action; retain their original install path.
- name: Use external node-gyp to avoid pnpm's bundled copy
if: steps.install-mode.outputs.shared != 'true'
run: |
npm install -g node-gyp@11.5.0
echo "npm_config_node_gyp=$(npm root -g)/node-gyp/bin/node-gyp.js" >> "$GITHUB_ENV"
- name: Install dependencies
if: steps.install-mode.outputs.shared != 'true'
run: pnpm install --frozen-lockfile
- name: Build Electron app for terminal perf
+4 -2
View File
@@ -26,6 +26,7 @@ jobs:
test:
name: tests node ${{ matrix.node }} ${{ matrix.shard.index }}/${{ matrix.shard.count }}
runs-on: ${{ inputs.runner }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
@@ -82,6 +83,7 @@ jobs:
matrix:
node: ${{ fromJSON(inputs.node_versions) }}
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
@@ -103,8 +105,8 @@ jobs:
- name: Install relay integration dependencies
working-directory: cloud
run: |
npx --yes pnpm@10.24.0 --filter '@orca-cloud/relay...' install --frozen-lockfile --ignore-scripts
npx --yes pnpm@10.24.0 --filter '@orca-cloud/relay^...' build
npx --yes pnpm@10.34.6 --filter '@orca-cloud/relay...' install --frozen-lockfile --ignore-scripts
npx --yes pnpm@10.34.6 --filter '@orca-cloud/relay^...' build
- name: Test relay integration contracts
env: