name: CI on: push: branches: [main] pull_request: branches: [main, "release/*"] schedule: - cron: "23 0-2,4-23 * * *" - cron: "23 3 * * *" workflow_dispatch: workflow_call: inputs: source_sha: description: Exact qualification source; must match the caller revision. required: true type: string permissions: contents: read concurrency: # Complete qualification finishes even while main receives more pushes. # Main feedback also finishes, coalescing newer pushes to one pending run. # Scheduled/manual main runs coalesce to one active and the latest pending; # release and label-triggered qualification use exact-revision groups. group: ci-${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || inputs.source_sha != '' || contains(github.event.pull_request.labels.*.name, 'ci:full')) && 'qualification' || 'feedback' }}-${{ inputs.source_sha || github.event.pull_request.number || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' && inputs.source_sha == '' && !contains(github.event.pull_request.labels.*.name, 'ci:full') }} env: HUSKY: "0" jobs: changes: name: Classify changes runs-on: ubuntu-24.04 timeout-minutes: 5 permissions: contents: read actions: read outputs: tier: ${{ steps.plan.outputs.tier }} qualification_run_id: ${{ steps.qualification.outputs.run_id }} code: ${{ steps.plan.outputs.code }} reverse_tools: ${{ steps.plan.outputs.reverse_tools }} sqlite: ${{ steps.plan.outputs.sqlite }} runtime: ${{ steps.plan.outputs.runtime }} package: ${{ steps.plan.outputs.package }} apple: ${{ steps.plan.outputs.apple }} fixtures: ${{ steps.plan.outputs.fixtures }} readiness: ${{ steps.plan.outputs.readiness }} docs: ${{ steps.plan.outputs.docs }} workflows: ${{ steps.plan.outputs.workflows }} browser: ${{ steps.plan.outputs.browser }} binary_layout: ${{ steps.plan.outputs.binary_layout }} evm: ${{ steps.plan.outputs.evm }} javascript_recovery: ${{ steps.plan.outputs.javascript_recovery }} recorded_crash: ${{ steps.plan.outputs.recorded_crash }} web_network: ${{ steps.plan.outputs.web_network }} web_runtime: ${{ steps.plan.outputs.web_runtime }} web_source_map: ${{ steps.plan.outputs.web_source_map }} nativeaot: ${{ steps.plan.outputs.nativeaot }} website: ${{ steps.plan.outputs.website }} matrix: ${{ steps.plan.outputs.matrix }} test_selection: ${{ steps.plan.outputs.test_selection }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: ref: ${{ inputs.source_sha || github.sha }} fetch-depth: 0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Bind qualification to the caller revision if: inputs.source_sha != '' env: SOURCE_SHA: ${{ inputs.source_sha }} run: | test "${SOURCE_SHA}" = "${GITHUB_SHA}" test "${SOURCE_SHA}" = "$(git rev-parse HEAD)" - name: Find a verified ancestor for push selection id: baseline if: github.event_name == 'push' && inputs.source_sha == '' env: GH_TOKEN: ${{ github.token }} HEAD_SHA: ${{ github.sha }} CI_REPOSITORY: ${{ github.repository }} CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} run: node scripts/ci/baseline.mjs - name: Admit scheduled full qualification id: qualification if: github.event_name == 'schedule' && inputs.source_sha == '' env: GH_TOKEN: ${{ github.token }} HEAD_SHA: ${{ github.sha }} CI_REPOSITORY: ${{ github.repository }} CI_REPOSITORY_ID: ${{ github.repository_id }} CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} CI_FORCE_FULL: ${{ github.event.schedule == '23 3 * * *' }} run: node scripts/ci/qualification.mjs - name: Select verification lanes id: plan env: BASE_SHA: ${{ steps.baseline.outputs.base || github.event.pull_request.base.sha }} BASELINE_REASON: ${{ steps.baseline.outputs.reason }} HEAD_SHA: ${{ inputs.source_sha || github.event.pull_request.head.sha || github.sha }} CI_FULL: ${{ inputs.source_sha != '' || contains(github.event.pull_request.labels.*.name, 'ci:full') }} CI_SKIP_FULL: ${{ steps.qualification.outputs.run_full == 'false' }} run: node scripts/ci/plan.mjs static: name: Static checks needs: changes if: ${{ !cancelled() && needs.changes.outputs.tier != 'reused' }} runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Require successful change classification env: CHANGES_RESULT: ${{ needs.changes.result }} run: test "${CHANGES_RESULT}" = "success" - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - name: Restore static task cache if: needs.changes.outputs.code == 'true' id: static-cache uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: .turbo/cache/ key: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-${{ github.sha }} # Turbo validates each task's inputs after restoring this snapshot. restore-keys: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}- - name: Validate workflow syntax and expressions if: needs.changes.outputs.workflows == 'true' run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 -shellcheck= -pyflakes= - name: Record static task source and execution identity continue-on-error: true if: needs.changes.outputs.code == 'true' env: CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: npm run ci:static-context - if: needs.changes.outputs.code == 'true' run: npm run check:ci - if: needs.changes.outputs.code == 'true' run: npm run verify:test-discovery - name: Check LLDB producer retention and launch contracts if: needs.changes.outputs.code == 'true' run: npm run test:python - if: needs.changes.outputs.code != 'true' run: npm run format:check - name: Save validated static task cache if: >- success() && needs.changes.outputs.code == 'true' && steps.npm-cache.outputs.can-save == 'true' && steps.static-cache.outputs.cache-hit != 'true' uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: .turbo/cache/ key: ${{ steps.static-cache.outputs.cache-primary-key }} - name: Upload complete static diagnostics if: always() && needs.changes.outputs.code == 'true' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: static-check-reports path: .cache/static-checks/ include-hidden-files: true if-no-files-found: ignore retention-days: 7 test-source: name: Source tests needs: changes if: needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'fast' runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - name: Run source tests without waiting for build run: npm run test:fast -- --reporter=default --reporter=blob env: VITEST_BLOB_LABEL: source - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: ${{ !cancelled() }} with: name: vitest-report-source path: .vitest/blob/* include-hidden-files: true if-no-files-found: error retention-days: 7 test-shard: name: Test shard (${{ matrix.shard }}) needs: [changes, build] if: needs.changes.outputs.code == 'true' runs-on: ubuntu-latest timeout-minutes: 30 strategy: # Failed PRs release the remaining matrix slots; qualification retains # every shard's diagnostics and coverage. Passing PRs run the full suite. fail-fast: ${{ github.event_name == 'pull_request' && needs.changes.outputs.tier == 'fast' }} matrix: # Fast feedback spends the same six test jobs on one source group and # five built groups; full qualification retains six complete shards. shard: ${{ fromJSON(needs.changes.outputs.tier == 'fast' && '["1/5","2/5","3/5","4/5","5/5"]' || '["1/6","2/6","3/6","4/6","5/6","6/6"]') }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: rea-test-runtime path: . - name: Run complete portable suite with coverage if: needs.changes.outputs.tier == 'full' run: npm run test:ci:shard:run -- --shard=${{ matrix.shard }} env: REA_CI_TEST_HISTORY: .cache/ci-test-history/test-results.json - name: Run built-dependent tests for fast feedback if: needs.changes.outputs.tier == 'fast' run: npm run test:built -- --reporter=default --reporter=blob --shard=${{ matrix.shard }} env: VITEST_BLOB_LABEL: built REA_CI_TEST_HISTORY: .cache/ci-test-history/test-results.json - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: ${{ !cancelled() }} with: name: vitest-report-built-${{ strategy.job-index }} path: .vitest/blob/* include-hidden-files: true if-no-files-found: error overwrite: true retention-days: 7 reverse-tools-quality: name: Reverse tools coverage and mutation needs: [changes, static] if: needs.changes.outputs.reverse_tools == 'true' runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - name: Run reverse tools tests with coverage run: npm run test:coverage:reverse-tools - name: Report reverse tools mutation results run: npm run test:mutation:reverse-tools build: name: Build package needs: changes # Reusable provider workflows consume this run's portable build artifact, # even when a workflow-only edit selects the lane without portable tests. if: >- needs.changes.outputs.code == 'true' || needs.changes.outputs.browser == 'true' || needs.changes.outputs.binary_layout == 'true' || needs.changes.outputs.evm == 'true' || needs.changes.outputs.javascript_recovery == 'true' || needs.changes.outputs.recorded_crash == 'true' || needs.changes.outputs.web_network == 'true' || needs.changes.outputs.web_runtime == 'true' || needs.changes.outputs.web_source_map == 'true' runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: read actions: read steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - name: Reuse portable build outputs across runs uses: ./.github/actions/portable-build-cache with: save: ${{ github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - run: npm run test:prepare - name: Find available trusted full-suite timing history id: history continue-on-error: true env: GH_TOKEN: ${{ github.token }} CI_REPOSITORY: ${{ github.repository }} CI_REPOSITORY_ID: ${{ github.repository_id }} CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} run: node scripts/ci/test-history.mjs - name: Download previous timing report if: steps.history.outputs.run-id != '' continue-on-error: true uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: test-timings run-id: ${{ steps.history.outputs.run-id }} github-token: ${{ github.token }} path: .cache/ci-test-history - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: rea-test-runtime path: | dist/ skills/ .cache/mcp-tool-catalog.json .cache/ci-test-history/test-results.json docs/public/product-catalog.json docs/verification/managed-conformance-*.json include-hidden-files: true if-no-files-found: error retention-days: 7 # test:prepare caches only portable JS, skill, and catalog outputs. # Native package jobs still run their build and prepack checks; Turbo # restores matching outputs or rebuilds when their inputs differ. - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: rea-build-cache path: .turbo/cache/ include-hidden-files: true if-no-files-found: error retention-days: 7 sqlite-runtime: # Preserve all selected coverage, but admit expensive host/provider work # only after static validation. Portable tests still provide early feedback. needs: [changes, static, build] if: needs.changes.outputs.sqlite == 'true' uses: ./.github/workflows/sqlite-runtime.yml test: name: Portable tests needs: [changes, test-source, test-shard] if: ${{ !cancelled() && needs.changes.outputs.tier != 'reused' }} runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Require successful change classification env: CHANGES_RESULT: ${{ needs.changes.result }} run: test "${CHANGES_RESULT}" = "success" - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 if: needs.changes.outputs.code == 'true' && success() - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 if: needs.changes.outputs.code == 'true' && success() with: node-version-file: .nvmrc - name: Cache npm package downloads if: needs.changes.outputs.code == 'true' && success() id: npm-cache uses: ./.github/actions/npm-cache - if: needs.changes.outputs.code == 'true' && success() run: npm ci - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 if: >- !cancelled() && needs.changes.outputs.code == 'true' && (needs.test-shard.result == 'success' || needs.test-shard.result == 'failure' || (needs.changes.outputs.tier == 'fast' && (needs.test-source.result == 'success' || needs.test-source.result == 'failure'))) with: path: .vitest/blob pattern: vitest-report-* merge-multiple: true - name: Merge coverage, JUnit, and timing reports id: merge if: >- !cancelled() && needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'full' && (needs.test-shard.result == 'success' || needs.test-shard.result == 'failure') run: >- npm run test:ci:merge -- --coverage.reportsDirectory=coverage --coverage.reporter=text --coverage.reporter=json-summary --coverage.reporter=lcov --reporter=default --reporter=junit --reporter=json --reporter=./scripts/ci/test-timing-reporter.mjs --outputFile.junit=test-results.xml --outputFile.json=test-results.json - name: Merge fast-suite JUnit and timing reports id: merge-fast if: >- !cancelled() && needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'fast' && (needs.test-shard.result == 'success' || needs.test-shard.result == 'failure' || needs.test-source.result == 'success' || needs.test-source.result == 'failure') run: >- npm exec -- vitest --merge-reports --reporter=default --reporter=junit --reporter=json --reporter=./scripts/ci/test-timing-reporter.mjs --outputFile.junit=test-results.xml --outputFile.json=test-results.json - name: Summarize slowest test files if: needs.changes.outputs.code == 'true' && !cancelled() && hashFiles('test-timings.json') != '' env: SHARD_RESULT: ${{ needs.test-shard.result }} SOURCE_REQUIRED: ${{ needs.changes.outputs.tier == 'fast' }} SOURCE_RESULT: ${{ needs.test-source.result }} MERGE_RESULT: ${{ needs.changes.outputs.tier == 'full' && steps.merge.outcome || steps.merge-fast.outcome }} CI_VERIFY_TEST_INVENTORY: "true" CI_TEST_SELECTION: ${{ needs.changes.outputs.test_selection }} CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: node scripts/ci/test-report.mjs - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: needs.changes.outputs.code == 'true' && !cancelled() && hashFiles('test-results.json') != '' with: name: test-results path: | test-results.xml test-results.json test-timings.json coverage/ .cache/ci-test-selection/report.json include-hidden-files: true if-no-files-found: error retention-days: 30 - name: Publish successful timing history if: needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'full' && success() && needs.test-shard.result == 'success' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: test-timings path: .cache/ci-test-history/test-results.json include-hidden-files: true if-no-files-found: error retention-days: 30 - name: Require successful test shards if: ${{ !cancelled() }} env: CODE_REQUIRED: ${{ needs.changes.outputs.code }} SHARD_RESULT: ${{ needs.test-shard.result }} SOURCE_REQUIRED: ${{ needs.changes.outputs.tier == 'fast' }} SOURCE_RESULT: ${{ needs.test-source.result }} run: | if [ "${CODE_REQUIRED}" = "true" ]; then test "${SHARD_RESULT}" = "success" || exit 1 test "${SOURCE_REQUIRED}" != "true" || test "${SOURCE_RESULT}" = "success" fi conformance-fixtures: name: Source-built conformance fixtures needs: [changes, static] if: needs.changes.outputs.fixtures == 'true' runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Verify fixtures from a clean checkout run: npm run verify:fixtures windows-native-build: name: Build bundled Windows x64 native controls needs: [changes, static] if: needs.changes.outputs.runtime == 'true' runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Prepare cross compiler run: | # Slow Azure archive transfers can exhaust the job before compilation. # Keep the same signed packages and avoid slow HTTP mirror fallback. for source in /etc/apt/apt-mirrors.txt /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources; do if [ -f "$source" ]; then sudo sed -i 's|azure.archive.ubuntu.com|archive.ubuntu.com|g; s|http://archive.ubuntu.com|https://archive.ubuntu.com|g' "$source" fi done sudo apt-get update sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64 - run: npm run build:windows-native - run: npm run verify:windows-native:artifact - run: node scripts/build-windows-native.mjs --inspection-fixture - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: windows-native-inspection-fixture path: .cache/windows-native-inspection-fixture/ if-no-files-found: error # The process boundary observations are independent of the addon: they # report the caller's and child's own token, environment and breakaway # results from a separate executable. Cross-build it in the lane that # already has MinGW so the Windows job can observe them too. - name: Build Windows process boundary fixture run: | mkdir -p .cache/windows-process-boundary x86_64-w64-mingw32-g++ -municode -std=c++17 -O2 -static -Wall -Wextra \ -o .cache/windows-process-boundary/process-boundary.exe \ tests/fixtures/windows/processBoundary.cc -ladvapi32 - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: windows-process-boundary-fixture path: .cache/windows-process-boundary/ if-no-files-found: error - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: windows-native-controls path: native/windows/build/ if-no-files-found: error package-e2e: name: Native package baseline (${{ matrix.platform }} ${{ matrix.architecture }}) needs: [changes, static, build, windows-native-build] if: needs.changes.outputs.runtime == 'true' strategy: fail-fast: false # The four platform jobs use independent hosted runners; keep each # runner's existing heap and threadpool limits while avoiding a queued # second platform wave. max-parallel: 4 matrix: ${{ fromJSON(needs.changes.outputs.matrix) }} runs-on: ${{ matrix.os }} timeout-minutes: 20 env: NODE_OPTIONS: "--max-old-space-size=1024" UV_THREADPOOL_SIZE: "2" steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - name: Verify native runtime identity env: EXPECTED_CI_PLATFORM: ${{ matrix.platform }} EXPECTED_CI_ARCHITECTURE: ${{ matrix.architecture }} run: >- node -e "const assert = require('node:assert/strict'); assert.equal(process.platform, process.env.EXPECTED_CI_PLATFORM); assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE); console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));" - run: npm ci - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: rea-build-cache path: .turbo/cache/ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: windows-native-controls path: native/windows/build/ - run: npm run verify:windows-native:artifact - name: Verify NativeAOT diagnostic applicability on this host run: npm run test:focused -- tests/evaluation/package/nativeAotDiagnostics.test.ts - run: npm run verify:package env: npm_config_cache: ${{ steps.npm-cache.outputs.path }} REA_VERIFIED_PACKAGE_DIRECTORY: ${{ needs.changes.outputs.tier == 'full' && matrix.platform == 'linux' && matrix.architecture == 'x64' && '.cache/verified-package' || '' }} - name: Verify real Node Inspector discovery and observation timeout-minutes: 10 run: npm run verify:inspector - name: Retain the exact qualified release package if: needs.changes.outputs.tier == 'full' && matrix.platform == 'linux' && matrix.architecture == 'x64' uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: verified-release-package path: .cache/verified-package/ include-hidden-files: true if-no-files-found: error overwrite: true retention-days: 7 apple-artifacts: name: Real Apple artifact CLI/MCP E2E needs: [changes, static, build] if: needs.changes.outputs.apple == 'true' runs-on: macos-15 timeout-minutes: 15 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: rea-build-cache path: .turbo/cache/ - name: Verify Apple artifact formats and metadata env: REA_ARTIFACT_NODE_COMPILE_CACHE: ${{ runner.temp }}/rea-artifact-node-cache run: | npm run verify:keyed-archive npm run verify:asset-catalog npm run verify:macos-bundle npm run verify:legacy-simulator npm run verify:apple-dispatch npm run verify:interface-builder - name: Run native Apple and process boundary regressions run: >- npm run test:focused -- tests/boundary/filesystem/installScript.test.ts tests/boundary/filesystem/nibNativeHierarchy.test.ts tests/boundary/filesystem/interfaceBuilderBundle.test.ts tests/boundary/cli/nativePlistCli.test.ts tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts tests/boundary/cli/nativePlistSelectionFailureCli.test.ts tests/boundary/cli/nativeDemangleCli.test.ts tests/boundary/mcp/nativeDemangleMcp.test.ts tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts tests/boundary/cli/nativeSignatureCli.test.ts tests/boundary/mcp/nativeSignatureTargetBindingMcp.test.ts src/process/capture/FilesystemSnapshot.test.ts tests/process-global/filesystemSnapshotFallbackCancellation.test.ts tests/boundary/process/darwinProcessRunTokenReader.test.ts tests/boundary/process/directAnalysisSignals.test.ts tests/boundary/process/processCaptureLifecycle.test.ts tests/boundary/process/processCli.test.ts tests/boundary/mcp/sessionMcp.test.ts reconstruction-readiness: name: Reconstruction readiness needs: [changes, static] if: needs.changes.outputs.readiness == 'true' runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - name: Run the public CLI/MCP reconstruction journey env: REA_READINESS_REPORT_PATH: readiness-report.json run: | set -o pipefail npm run verify:readiness | tee readiness-summary.json - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: ${{ !cancelled() }} with: name: reconstruction-readiness path: | readiness-summary.json readiness-report.json if-no-files-found: error retention-days: 30 windows-curated: name: Windows capability and package lane needs: [changes, static, build, windows-native-build] if: needs.changes.outputs.package == 'true' runs-on: windows-latest timeout-minutes: 25 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: rea-build-cache path: .turbo/cache/ - run: npm run build:cached - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: windows-native-controls path: native/windows/build/ - run: npm run verify:windows-native:artifact - name: Record named Windows host capabilities run: node scripts/windows-capabilities.mjs | Out-File -Encoding utf8NoBOM windows-capabilities.json - name: Validate Windows host capability report run: >- node -e "const report = JSON.parse(require('node:fs').readFileSync('windows-capabilities.json', 'utf8')); if (report.platform !== 'win32' || report.architecture !== 'x64') throw new Error('expected a Windows x64 capability report');" # Keep this list aligned with tests/process-global/curatedWindowsLane.test.ts, # which fails when any path below is renamed or removed. Vitest silently # ignores unmatched paths, so a stale entry would otherwise report green # while skipping coverage. # # The matching native artifact establishes real Job Object and private # runtime boundaries for these fixtures. Ghidra and Java are not needed; # real engine claims remain in the separate controlled-provider workflow. - name: Run curated Windows capability tests run: >- npx vitest run --coverage.enabled=false src/process/capture/FilesystemSnapshot.test.ts tests/process-global/filesystemSnapshotFallbackCancellation.test.ts tests/boundary/filesystem/binaryTarget.test.ts tests/boundary/filesystem/safeOutputTree.test.ts tests/boundary/providers/native/artifactProvider.archiveSafety.test.ts tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts tests/boundary/filesystem/setupUnavailableProvider.test.ts tests/boundary/filesystem/windowsNpxRegistration.test.ts tests/boundary/android/workspaceGuardianRetry.test.ts tests/acceptance/setup/homeDirectoryFallback.test.ts tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts tests/boundary/filesystem/webScriptManifestPaths.test.ts tests/boundary/process/ProcessEvidence.host.test.ts tests/acceptance/webModuleCli.test.ts tests/acceptance/webSourceLocationCli.test.ts tests/boundary/cli/referenceSourceImportStatus.test.ts tests/boundary/filesystem/asarInventoryFreshness.test.ts tests/boundary/filesystem/javascript-applications/javascriptArtifactReconstruction.test.ts tests/conformance/providers/ghidra/windowsCapabilities.test.ts src/ghidra/GhidraInstallation.test.ts tests/boundary/filesystem/ghidraJavaProbe.test.ts tests/boundary/providers/ghidra/ghidraTransport.test.ts tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts tests/boundary/providers/ghidra/ghidraLauncher.test.ts tests/boundary/providers/hopper/hopperClient.cleanupRetry.test.ts src/windows/WindowsNativeLoader.test.ts src/windows/WindowsOwnedProcess.test.ts src/ghidra/GhidraLauncherEnvironment.test.ts src/process/ProcessOwnership.test.ts src/process/ProcessOwnership.part2.test.ts src/process/ProcessOwnership.part3.test.ts src/process/ProcessOwnership.validation.test.ts src/application/Update.test.ts tests/process-global/updateMaintenance.test.ts tests/conformance/providers/ghidra/realGhidraWindowsWorkflow.test.ts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: windows-process-boundary-fixture path: .cache/windows-process-boundary/ # Passing the fixture adds the breakaway, caller/child token, environment # semantics and ancestor reparse observations, which the lane skips # entirely when it is absent. - run: >- npm run verify:windows-native -- . "${{ github.workspace }}\.cache\windows-process-boundary\process-boundary.exe" - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: windows-native-inspection-fixture path: .cache/windows-native-inspection-fixture/ - name: Verify native inspection failure ownership and recovery run: node scripts/verify-windows-native-inspection.mjs - name: Verify real Node Inspector discovery and observation timeout-minutes: 10 env: NODE_OPTIONS: "--max-old-space-size=1024" UV_THREADPOOL_SIZE: "2" run: npm run verify:inspector - run: npm run verify:package:windows - name: Verify native directory identity on Node 24 run: >- npx vitest run --coverage.enabled=false tests/boundary/filesystem/directoryArtifactReaderOpen.test.ts tests/boundary/filesystem/javascriptPackagePrecedence.test.ts tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 22.19.0 - name: Verify native path boundaries on Node 22 run: >- npx vitest run --coverage.enabled=false tests/boundary/filesystem/directoryArtifactReaderOpen.test.ts tests/boundary/filesystem/javascriptPackagePrecedence.test.ts tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts tests/boundary/process/sourceMapUncHosts.test.ts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: name: windows-capabilities path: windows-capabilities.json retention-days: 30 docs: name: Docs Generation needs: changes if: needs.changes.outputs.docs == 'true' runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Cache npm package downloads id: npm-cache uses: ./.github/actions/npm-cache - run: npm ci - run: npm run docs:check - name: Generation leaves authored files unchanged run: git diff --exit-code - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: generated-docs path: | docs/public/product-catalog.json docs/verification/managed-conformance-*.json skills/reverse-engineer-anything if-no-files-found: error browser: needs: [changes, static, build] if: needs.changes.outputs.browser == 'true' uses: ./.github/workflows/real-browser.yml with: build_cache_artifact: rea-build-cache binary_layout: needs: [changes, static, build] if: needs.changes.outputs.binary_layout == 'true' uses: ./.github/workflows/real-binary-layout.yml with: build_cache_artifact: rea-build-cache evm: needs: [changes, static, build] if: needs.changes.outputs.evm == 'true' uses: ./.github/workflows/real-evm-interface.yml with: build_cache_artifact: rea-build-cache javascript_recovery: needs: [changes, static, build] if: needs.changes.outputs.javascript_recovery == 'true' uses: ./.github/workflows/real-javascript-recovery.yml with: build_cache_artifact: rea-build-cache recorded_crash: needs: [changes, static, build] if: needs.changes.outputs.recorded_crash == 'true' uses: ./.github/workflows/real-recorded-crash.yml with: build_cache_artifact: rea-build-cache web_network: needs: [changes, static, build] if: needs.changes.outputs.web_network == 'true' uses: ./.github/workflows/real-web-network-captures.yml with: build_cache_artifact: rea-build-cache web_runtime: needs: [changes, static, build] if: needs.changes.outputs.web_runtime == 'true' uses: ./.github/workflows/real-web-runtime.yml with: build_cache_artifact: rea-build-cache web_source_map: needs: [changes, static, build] if: needs.changes.outputs.web_source_map == 'true' uses: ./.github/workflows/real-web-source-map.yml with: build_cache_artifact: rea-build-cache nativeaot: needs: [changes, static] if: needs.changes.outputs.nativeaot == 'true' uses: ./.github/workflows/nativeaot-fixtures.yml website: needs: [changes, static] if: needs.changes.outputs.website == 'true' uses: ./.github/workflows/website-check.yml required: name: CI required # Report failed dependencies, but release superseded PR concurrency slots. if: ${{ !cancelled() }} needs: [ changes, static, build, reverse-tools-quality, test-shard, test-source, sqlite-runtime, test, conformance-fixtures, windows-native-build, package-e2e, apple-artifacts, reconstruction-readiness, windows-curated, docs, browser, binary_layout, evm, javascript_recovery, recorded_crash, web_network, web_runtime, web_source_map, nativeaot, website, ] runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Require every selected lane to pass id: gate env: CI_JOBS: ${{ toJSON(needs) }} CI_QUALIFICATION_DIRECTORY: .cache/ci-qualification run: node scripts/ci/required.mjs - name: Upload full qualification receipt artifact if: needs.changes.outputs.tier == 'full' && success() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: ${{ steps.gate.outputs.receipt_name }} path: ${{ steps.gate.outputs.receipt_path }} if-no-files-found: error overwrite: true retention-days: 7