Skip to content

feat(runtime): receive chunked worker blobs #14259

feat(runtime): receive chunked worker blobs

feat(runtime): receive chunked worker blobs #14259

Workflow file for this run

name: Test
on:
push:
branches:
- main
paths:
- "web/**"
- "electron/**"
- "packages/**"
- "reliability/**"
- "mobile/**"
- "examples/**"
- "scripts/**"
- "Dockerfile"
- ".dockerignore"
- "package.json"
- "package-lock.json"
- "tsconfig.json"
- "tsconfig.*.json"
- "turbo.json"
- ".nvmrc"
- ".github/workflows/test.yml"
- ".github/workflows/quality-checks.yml"
- ".github/actions/setup-build/**"
pull_request:
branches:
- main
# No `paths` filter here: this workflow's "Quality Gate" job is a REQUIRED
# status check. A path-filtered required check is left Pending (never runs)
# when a PR push/merge touches no matching path — e.g. a clean "merge main"
# commit — which blocks the PR and auto-merge indefinitely. Running the gate
# on every PR keeps the required check from getting stuck. (The `push` to
# main above keeps its path filter — there is no required-check to block.)
# Cancel superseded runs for the same branch/PR; the latest push is what matters.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Does this push touch anything but prose? A documentation-only change has
# nothing for typecheck, lint, the test suites, the image build or the
# browser E2E runs to say, and every leg it starts holds a runner other PRs
# queue behind (the account cap is ~20 concurrent jobs). The workflow still
# triggers on every PR — see the `paths` note above — so the required check
# is always reported; the legs inside it skip instead, and a skipped job
# counts as a pass for a required check.
#
# The prose set is deliberately narrow: `docs/**` (the Jekyll site), the
# Markdown at the repo root, the agent instruction files anywhere, and the
# Markdown under `.github/`. Markdown that code reads at run time — a
# sandbox pack's `SKILL.md`, a package `README.md` — is NOT in it and keeps
# running the full gate.
#
# Fails open: the step is `continue-on-error` and the output falls back to
# "true", so a filter that cannot resolve a base runs everything rather than
# waving a change through.
#
# It also decides whether the diff can reach the root workspace at all.
# `marketing/` is a separate npm project — its own lockfile, its own install,
# built and deployed by marketing-ci.yml — and nothing in this workflow
# touches it. A marketing-only PR used to build ~55 backend packages,
# typecheck three apps, run every suite, build the container image and drive
# two browser E2E jobs; with an account-wide cap of ~20 concurrent jobs, that
# is time the rest of the queue spends waiting. `shared` is false for such a
# diff and every leg skips, exactly as it does for prose.
#
# `mobile/` is NOT carved out, though it is also a separate npm project. Its
# typecheck resolves `@nodetool-ai/websocket`'s router types (not a mobile
# dependency at all) and `@nodetool-ai/protocol` through the root workspace,
# so it is not separable from this gate without changing how mobile resolves
# those packages. See the workflows README.
changes:
name: changes
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
code: ${{ steps.filter.outputs.code || 'true' }}
shared: ${{ steps.filter.outputs.shared || 'true' }}
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
fetch-depth: 1
- name: Detect non-documentation changes
id: filter
continue-on-error: true
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
with:
filters: |
code:
- '!{docs/**,*.md,**/AGENTS.md,**/CLAUDE.md,.github/**/*.md}'
shared:
- '!{docs/**,*.md,**/AGENTS.md,**/CLAUDE.md,.github/**/*.md,marketing/**,.github/workflows/marketing-ci.yml}'
quality:
name: Quality Gate (npm run check)
needs: changes
# `always()` plus the explicit `== 'false'` test below so a `changes` job
# that never reported (infrastructure failure, not a filter result) runs the
# full gate. Only an explicit "this diff is prose" skips work — a required
# check must never go green because the job that decides was broken.
if: always()
uses: ./.github/workflows/quality-checks.yml
# A called workflow's permissions are capped by the caller's, so the gate's
# `docker` leg only gets its read-only GHCR buildcache access if it is
# granted here too.
permissions:
contents: read
packages: read
with:
# false → every check leg (deps/typecheck/lint/tests) runs to completion
# so each reports its own red/green instead of the first failure masking
# the rest. The required check is the reusable workflow's `quality` gate
# job, surfaced unchanged as "Quality Gate (npm run check) / quality".
fail-fast: false
skip-tests: false
# A documentation-only push skips every leg inside the gate. The gate job
# itself still runs and reports, so the required check lands green.
docs-only: ${{ needs.changes.outputs.code == 'false' }}
# Same fail-safe reading as `docs-only`: the output defaults to 'true',
# so a filter that could not resolve a base runs the whole gate. False
# only for a diff confined to marketing/, whose gate is marketing-ci.yml.
shared: ${{ needs.changes.outputs.shared != 'false' }}
# Heavy jobs run concurrently with the quality gate (both wait only on the
# seconds-long `changes` job) rather than after it, so
# the critical path is max(quality, integration, e2e) instead of
# quality + max(integration, e2e). Trade-off: a PR that fails a static check
# still spends these runner minutes. They build packages themselves (they are
# in this workflow, not the reusable quality-checks one, so they can't consume
# its build artifact without depending on the whole gate — which would undo
# the decoupling).
integration:
name: Workflow Integration Tests
needs: changes
# Same fail-safe reading as the gate above: skip only on an explicit
# "prose", or an explicit "this diff never leaves marketing/" — it runs
# shipped-workflow execution out of packages/base-nodes, which marketing
# cannot reach.
if: ${{ always() && needs.changes.outputs.code != 'false' && needs.changes.outputs.shared != 'false' }}
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Node, install deps, build packages
uses: ./.github/actions/setup-build
- name: Run shipped-workflow execution tests
run: npm run test:integration
workflow-runner-e2e:
name: Workflow Runner Browser E2E
needs: changes
if: ${{ always() && needs.changes.outputs.code != 'false' && needs.changes.outputs.shared != 'false' }}
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up Node, install deps, build packages
# mesa-vulkan-drivers ships lavapipe (CPU Vulkan ICD) so headless
# Chromium can acquire a WebGPU adapter on GPU-less CI runners. The
# workflow-runner browser harness uses it to drive shader-pool tests
# (color.brightnessContrast, etc.) against the same module catalog
# the server uses on Node/Dawn.
uses: ./.github/actions/setup-build
with:
extra-apt: mesa-vulkan-drivers
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Playwright Chromium browser
run: npx playwright install --with-deps chromium
working-directory: packages/workflow-runner
- name: Run workflow-runner browser E2E tests
run: npm run test:e2e --workspace=@nodetool-ai/workflow-runner
- name: Upload Playwright report on failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: workflow-runner-playwright-report
path: packages/workflow-runner/playwright-report
retention-days: 7