feat(mcp): serve task-augmented tool calls — CreateTaskResult, tasks/get, tasks/result, tasks/cancel, tasks/list (#369) #1971
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| # Merge queue: every job that is a required check on main must also run on | |
| # merge_group, or a queued entry waits until the queue times it out. The | |
| # event never fires while this repository is user-owned (GitHub offers the | |
| # queue only to organization-owned repositories: creating a `merge_queue` | |
| # ruleset here returns 422 "Invalid rule 'merge_queue'"), so this trigger | |
| # is dormant until the repository moves to an organization. | |
| merge_group: | |
| branches: [main] | |
| # Nightly release-boundary matrix (packed-matrix job): the scaffolder | |
| # template tests beyond the per-PR minimal-template smoke. | |
| schedule: | |
| - cron: '17 6 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| # PR pushes cancel the superseded run of the same PR (only the latest commit | |
| # matters). Pushes to main, merge-queue entries, the nightly schedule, and | |
| # manual dispatches get SHA-keyed groups with no cancellation, so a new main | |
| # push can never kill an in-flight main, queue, or nightly run. Same pattern | |
| # as package-preview.yml. | |
| concurrency: | |
| group: ci-${{ github.event.pull_request.number || github.sha }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| # PR-only docs allowlist: docs/**, agent-patterns/**, website/** (the | |
| # Rspress site, validated by docs.yml), .changeset/*.md, and top-level *.md. | |
| # Nested markdown elsewhere is code because examples and packages contain | |
| # compiled SKILL.md artifacts, and package markdown affects npm pack audits. | |
| # Classification fails open so uncertain PRs run every heavy job; pushes to | |
| # main and merge-queue entries never skip any job based on changed paths | |
| # (this job does not run for them, and the heavy jobs treat the missing | |
| # output as "run"). | |
| # Path rules and fail-open listing checks live in | |
| # scripts/classify-docs-only.mjs (unit-tested). This job sparse-checkouts | |
| # only that script so the vendored Effect subtree never lands on the | |
| # classify critical path. | |
| changes: | |
| if: github.event_name == 'pull_request' | |
| name: Detect changed paths | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| pull-requests: read | |
| outputs: | |
| docs_only: ${{ steps.classify.outputs.docs_only }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| filter: blob:none | |
| persist-credentials: false | |
| sparse-checkout: | | |
| scripts/classify-docs-only.mjs | |
| sparse-checkout-cone-mode: false | |
| - name: Classify changed files | |
| id: classify | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| set -euo pipefail | |
| files_file="$(mktemp)" | |
| trap 'rm -f "$files_file"' EXIT | |
| if ! changed_files="$( | |
| gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \ | |
| --jq '.changed_files' | |
| )"; then | |
| echo "Could not read the PR changed-files count; failing open so heavy jobs run." | |
| node scripts/classify-docs-only.mjs --listing-error | |
| exit 0 | |
| fi | |
| if ! gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files" \ | |
| --paginate \ | |
| --jq '.[] | [.filename, (.previous_filename // "")] | @tsv' \ | |
| > "$files_file"; then | |
| echo "Could not list changed files; failing open so heavy jobs run." | |
| node scripts/classify-docs-only.mjs --listing-error | |
| exit 0 | |
| fi | |
| node scripts/classify-docs-only.mjs \ | |
| --changed-files-count "$changed_files" \ | |
| --listing "$files_file" | |
| # Builds and checks every public example through its own toolchain. | |
| examples-check: | |
| needs: changes | |
| if: >- | |
| ${{ !cancelled() && github.event_name != 'schedule' && | |
| (github.event_name != 'pull_request' || needs.changes.outputs.docs_only != 'true') }} | |
| name: Examples check (Node 22.19) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/setup@v2 | |
| with: | |
| cache: true | |
| install: false | |
| runtime: node@22.19.0 | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm examples:check | |
| verify: | |
| needs: changes | |
| if: >- | |
| ${{ !cancelled() && github.event_name != 'schedule' && | |
| (github.event_name != 'pull_request' || needs.changes.outputs.docs_only != 'true') }} | |
| name: Verify (Node ${{ matrix.node-version }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| # Measured PR Verify cost was 3 legs × ~7 minutes, about 75% of PR | |
| # runner-minutes. PRs and merge-queue entries run newest-LTS Node 24 (the | |
| # "Verify (Node 24)" context is the required check on main); every main | |
| # push and manual dispatch still gates on the full supported matrix. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node-version: ${{ (github.event_name == 'pull_request' || github.event_name == 'merge_group') && fromJSON('["24"]') || fromJSON('["22.19.0","24","26"]') }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/setup@v2 | |
| with: | |
| cache: true | |
| install: false | |
| runtime: node@${{ matrix.node-version }} | |
| - run: pnpm install --frozen-lockfile | |
| # Tests launch branded Chrome (`channel: 'chrome'`), which Playwright | |
| # installs system-wide via apt — never into ~/.cache/ms-playwright, so | |
| # a browser cache cannot help. In CI, `playwright install chrome` | |
| # unconditionally removes and re-downloads ~120 MB of Chrome per job, | |
| # while ubuntu-latest images already ship Google Chrome stable with its | |
| # OS deps. Reuse it; fall back to the full install (browser + deps) if | |
| # the runner image ever drops it. | |
| - name: Ensure branded Chrome for Playwright | |
| run: | | |
| if command -v google-chrome >/dev/null 2>&1; then | |
| echo "Using preinstalled $(google-chrome --version)" | |
| else | |
| pnpm exec playwright install --with-deps chrome | |
| fi | |
| # Build first: checked-in suites and API tests import the package's built type | |
| # declarations, so typecheck requires dist (same order as `pnpm check`). | |
| # publint runs inside each publishable package's `rslib build` | |
| # (rsbuild-plugin-publint, throwOn: 'warning'), so a manifest warning | |
| # already fails this step; there is no separate publint step. | |
| - run: pnpm build | |
| - run: pnpm typecheck | |
| - run: pnpm lint | |
| - run: pnpm test | |
| # Required-check anchor for the Verify matrix. When a matrix job is skipped | |
| # (docs-only PRs), GitHub reports it under its unexpanded name | |
| # "Verify (Node ${{ matrix.node-version }})", so a branch protection that | |
| # requires "Verify (Node 24)" never sees that context and blocks the PR for | |
| # good. This job has a static name and always runs after Verify: it passes | |
| # when every leg passed or the matrix was skipped (a skipped job satisfies | |
| # a required check, like the other docs-only skips) and fails when a leg | |
| # failed or was cancelled. Require "Verify gate" on main, not a leg name. | |
| verify-gate: | |
| needs: verify | |
| if: always() && github.event_name != 'schedule' | |
| name: Verify gate | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require every Verify leg to have passed or been skipped | |
| env: | |
| VERIFY_RESULT: ${{ needs.verify.result }} | |
| run: | | |
| echo "Verify matrix result: $VERIFY_RESULT" | |
| case "$VERIFY_RESULT" in | |
| success|skipped) exit 0 ;; | |
| *) | |
| echo "::error::Verify matrix result is '$VERIFY_RESULT'; see the Verify (Node …) jobs." | |
| exit 1 | |
| ;; | |
| esac | |
| release-gates: | |
| needs: changes | |
| if: >- | |
| ${{ !cancelled() && github.event_name != 'schedule' && | |
| (github.event_name != 'pull_request' || needs.changes.outputs.docs_only != 'true') }} | |
| name: Release gates (Node 22.19) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/setup@v2 | |
| with: | |
| cache: true | |
| install: false | |
| runtime: node@22.19.0 | |
| - run: pnpm install --frozen-lockfile | |
| # Reuse the runner image's Chrome; see the Verify job for the rationale. | |
| - name: Ensure branded Chrome for Playwright | |
| run: | | |
| if command -v google-chrome >/dev/null 2>&1; then | |
| echo "Using preinstalled $(google-chrome --version)" | |
| else | |
| pnpm exec playwright install --with-deps chrome | |
| fi | |
| # Per-PR packed pool: single pack+install proofs plus the | |
| # minimal-template scaffolder smoke. The full template matrix runs in | |
| # the nightly packed-matrix job and in pre-publish `pnpm check:release`. | |
| - run: pnpm check:release:ci | |
| # Binary-gated real-host install proofs. The proof suites skip their Claude | |
| # and Codex legs when the CLIs are absent, which is how #364 changed the | |
| # Codex `interface.logo` emission and broke both proofs on main without CI | |
| # noticing (#367/#368 repaired them from local runs). This job installs the | |
| # exact CLI versions pinned in each adapter's schema PROVENANCE.json | |
| # (`hostCli`, kept equal to `observedCliVersion` by scripts/host-cli-pins.mjs), | |
| # fails closed if `claude`/`codex --version` differs from the pin, and runs | |
| # the source-built proofs, the packed-tarball proofs, and the packed Claude | |
| # plugin validation. Nothing here needs a login: every proof runs against an | |
| # isolated HOME / CLAUDE_CONFIG_DIR / CODEX_HOME and no secrets are passed. | |
| # Signed-in smokes (`claude -p`, `codex exec`, the Eval harnesses) stay in | |
| # the opt-in native-host-smoke workflow. | |
| host-install-proofs: | |
| needs: changes | |
| if: >- | |
| ${{ !cancelled() && github.event_name != 'schedule' && | |
| (github.event_name != 'pull_request' || needs.changes.outputs.docs_only != 'true') }} | |
| name: Host install proofs (Node 22.19) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/setup@v2 | |
| with: | |
| cache: true | |
| install: false | |
| runtime: node@22.19.0 | |
| - run: pnpm install --frozen-lockfile | |
| - name: Read pinned host CLI versions | |
| id: pins | |
| run: node scripts/host-cli-pins.mjs print | |
| # The global npm prefix holding both CLIs (Claude Code and Codex ship | |
| # platform-native binaries), keyed by OS, arch, and the exact pins | |
| # (package name and version per host) so a deliberate re-pin of either | |
| # misses the cache and installs fresh. | |
| - name: Restore pinned host CLIs | |
| id: host-cli-cache | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ runner.temp }}/host-cli | |
| key: host-cli-${{ runner.os }}-${{ runner.arch }}-${{ steps.pins.outputs.pins }} | |
| - name: Install pinned host CLIs | |
| if: steps.host-cli-cache.outputs.cache-hit != 'true' | |
| run: node scripts/host-cli-pins.mjs install --prefix "$RUNNER_TEMP/host-cli" | |
| # ubuntu runner: npm's global executables live in <prefix>/bin (the | |
| # script's globalBinDirectory rule; Windows would be the prefix itself). | |
| - name: Expose host CLIs on PATH | |
| run: echo "$RUNNER_TEMP/host-cli/bin" >> "$GITHUB_PATH" | |
| - name: Verify host CLI versions match the schema pins | |
| run: node scripts/host-cli-pins.mjs verify | |
| - run: pnpm build | |
| - name: Host install proofs (source-built bundle) | |
| run: pnpm test:host-install | |
| - name: Host install proofs (packed tarball) | |
| run: pnpm test:host-install:packed | |
| - name: Packed Claude plugin validation proof | |
| env: | |
| AGENT_BUNDLE_PACKAGE_PREBUILT: '1' | |
| run: pnpm test:packed:native | |
| # Release-boundary scaffolder template matrix (mcp-server, cli-tool) plus | |
| # the full packed pool — the nightly form of pre-publish `check:release`. | |
| packed-matrix: | |
| if: github.event_name == 'schedule' | |
| name: Packed release matrix (Node 22.19) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 40 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/setup@v2 | |
| with: | |
| cache: true | |
| install: false | |
| runtime: node@22.19.0 | |
| - run: pnpm install --frozen-lockfile | |
| # Reuse the runner image's Chrome; see the Verify job for the rationale. | |
| - name: Ensure branded Chrome for Playwright | |
| run: | | |
| if command -v google-chrome >/dev/null 2>&1; then | |
| echo "Using preinstalled $(google-chrome --version)" | |
| else | |
| pnpm exec playwright install --with-deps chrome | |
| fi | |
| - run: pnpm check:release | |
| # Nightly evidence-capture pool: documentation-artifact browser | |
| # journeys whose behavioral contracts are already proven per PR by the | |
| # integration pool (see nightlyEvidenceTestFiles). | |
| - run: pnpm test:evidence | |
| rsc-runtime-micro-eval: | |
| # Deterministic end-to-end spot-check of the built RSC runtime artifacts | |
| # (hook -> RSC worker -> shared kernel state -> MCP tool lowering) without | |
| # any real Claude/Codex host. Login-free real-host install proofs run in | |
| # the host-install-proofs job; signed-in native smokes stay skip-gated in | |
| # the manually dispatched native-host-smoke workflow on purpose. | |
| needs: changes | |
| if: >- | |
| ${{ !cancelled() && github.event_name != 'schedule' && | |
| (github.event_name != 'pull_request' || needs.changes.outputs.docs_only != 'true') }} | |
| name: RSC runtime micro-eval (Node 22.19) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: pnpm/setup@v2 | |
| with: | |
| cache: true | |
| install: false | |
| runtime: node@22.19.0 | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm eval:spot | |
| dependency-review: | |
| if: github.event_name == 'pull_request' | |
| name: Dependency review | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/dependency-review-action@v5 |