Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
name: CI
# Modelled on autonomous-harness-desktop's ci.yml. Like that one, this runs ON DEMAND only.
#
# It used to run on every pull request as well (2026-09-10 → 2026-09-16). That was switched off
# deliberately: the suite is timing-sensitive on the shared ubuntu runner (real tmux server, shell
# scripts, sqlite3 polls), so a red check on a PR said more about the runner than about the change,
# and every contributor had to read the log to find out which. The verdict on a branch is still one
# click away — Actions -> CI -> Run workflow, pick the branch — which is what a reviewer does when
# they want it. If per-PR CI comes back, use `pull_request`, never `pull_request_target`: a fork's PR
# must run with a read-only token and no secrets, since this job executes the contributor's code.
#
# There is no `push` trigger either. Nobody pushes to main directly (CONTRIBUTING.md step 2) and
# merges are squashed, so a push run would re-test a commit somebody already ran by hand.
on:
# The only way this runs on its own: Actions -> CI -> Run workflow, on any branch.
workflow_dispatch:
# Callable as a reusable workflow. release.yml used to gate on this and take the Node pin from
# its output; it no longer does (see the note above `publish` there) and pins the same version
# itself, so the output below is kept only for any future caller.
workflow_call:
outputs:
node-version:
description: The Node the suite ran on. release.yml bundles the release with it.
value: ${{ jobs.typecheck-test.outputs.node-version }}
permissions:
contents: read
concurrency:
group: cli-ci-${{ github.workflow }}-${{ github.ref }}
# Every run here was started by a person or by a release, and is the record of whether that commit
# was green — never killed to make room for a newer one.
cancel-in-progress: false
jobs:
# Typecheck, the full suite, and the package updater's complete-coverage gate.
typecheck-test:
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
node-version: ${{ steps.pin.outputs.node-version }}
defaults:
run:
working-directory: cli
steps:
- uses: actions/checkout@v7
# THE runtime pin — release.yml's `publish` job carries the same value; bump them together.
#
# It is not a taste in Node versions: it is the version of the MANAGED runtime the shipped CLI
# actually executes on. `harness/runtime/metadata.json` in the release bucket serves
# node v22.23.2 for darwin-arm64, darwin-x64, linux-x64 and linux-arm64, `install.sh` unpacks
# it under ~/.harness/runtime, and the launcher names that binary absolutely — so the user's
# own node, if any, never runs this bundle. Testing on anything else measures a runtime no
# machine in the field has. Bump this together with `make upload-node-runtime` in
# autonomous-harness-desktop, never on its own.
#
# (package.json's `engines` says >=20, which is the floor a THIRD PARTY may run it on. A floor
# is not a pin: a floating 22.x turns an unrelated upstream release into a red build on an
# untouched commit.)
- id: pin
run: echo "node-version=22.23.2" >> "$GITHUB_OUTPUT"
- uses: actions/setup-node@v7
with:
node-version: ${{ steps.pin.outputs.node-version }}
cache: npm
# Relative to the repo root, not to `defaults.working-directory` — this is an action
# input, not a `run:`.
cache-dependency-path: cli/package-lock.json
# TWO lockfiles are tracked here, and only one of them has ever been guarded.
#
# `package-lock.json` is the one the released bytes are built from — every build path in this
# repo shells out to `npm run bundle` (install-cli.sh, upload-cli.sh, remote-machine.sh) — and
# `npm ci` below refuses to run when it disagrees with package.json, so npm's half checks
# itself.
#
# `pnpm-lock.yaml` is checked by nothing, and on 2026-09-09 that cost a maintainer an
# afternoon: `bonjour-service` was added to package.json with only package-lock.json updated,
# main went green, and `make install-cli` then died at `Could not resolve "bonjour-service"`
# on every machine whose node_modules came from pnpm. Deleting the file would also fix that;
# keeping it and checking it is the choice made here, because contributors do use it.
#
# `--lockfile-only` resolves without installing (~5s) and writes nothing under --frozen-lockfile.
# Verified against this repo: it exits 1 on the stale lockfile that shipped in c4de318, naming
# the added dependency, and 0 once it is regenerated.
- name: Check pnpm-lock.yaml still matches package.json
run: |
npm i -g pnpm@11 >/dev/null
pnpm install --frozen-lockfile --lockfile-only
# `npm ci`, not `npm install`: the lockfile is the input, not a suggestion.
- run: npm ci
- run: npx tsc --noEmit
# 1,871 tests over 144 files, ~30s. The four `*.real.spec.ts` / `localE2e.spec.ts` suites that
# need a live tmux, Herdr or Cursor are gated behind RUN_* env vars and stay skipped here —
# `npm run test:tmux-real` and friends are how those are run, on a machine that has them.
- run: npx vitest run
- name: Require complete harness updater coverage
run: npm run test:dsh-updates
279918da1This file is pinned to the selected release. Download the original to open it in your design tool.