name: CI on: push: branches: [main, master] pull_request: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: checks: runs-on: ubuntu-latest strategy: fail-fast: false matrix: node: [24, 26] name: node ${{ matrix.node }} steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: ${{ matrix.node }} - name: Enable corepack (pnpm) run: corepack enable - name: Install (immutable) run: pnpm install --frozen-lockfile - name: Constraints run: pnpm run constraints # Before lint: the type-aware ESLint config resolves vendor packages via # their built declarations (tsconfig.typecheck.json -> vendor/*/lib), # which `pnpm run typecheck` emits. Lint on a fresh checkout would otherwise # see unresolved types and erupt with no-unsafe-* errors. - name: Typecheck (src + tests + examples) run: pnpm run typecheck - name: Lint run: pnpm run lint # Doc-sync gates (doc-sync-enforcement RFC). doc-typecheck compiles the fenced ts blocks in # the docs and resolves vendor packages via their built declarations, which # the typecheck step above emits — so it runs after typecheck. The cordis # catalog freshness check, type-equiv check, and markdown wrap/link checks # only read source. Same `doc-sync` script the pre-push hook runs # (quality-gates RFC: one source of truth). - name: Doc-sync gates (doc code blocks + cordis catalog + type-equiv + markdown wrap/links) run: pnpm run doc-sync # Module-graph freshness: regenerate docs/module-graph.md from the # packages' peerDependencies and fail if it differs from the committed # file. Only reads source package.json — no build needed. - name: Module-graph freshness run: pnpm run verify-module-graph - name: Tests with coverage gate (per-file 100%) run: pnpm run test:coverage # ACP snapshot tests (acp-snapshot-tests RFC): boot the real acp-agent # subprocess and replay recorded session-log fixtures, diffing the # normalized stdout transcript + re-persisted log against committed # goldens. KEYLESS by design — the same `test:snapshot` script the pre-push # hook runs (one source of truth), so the full-transcript regression net # is part of every PR gate, not just local pre-push. - name: Snapshot tests (ACP transcript replay) run: pnpm run test:snapshot # Before hygiene: publint validates the packed artifacts (lib/index.js), # which only the tsdown bundling step emits. - name: Build (tsc -b + tsdown bundles) run: pnpm run build - name: Hygiene (knip + publint) run: pnpm run knip && pnpm run publint - name: Demo smoke test run: | set -euo pipefail out=$(printf 'echo ci smoke\n' | timeout 60 node --expose-internals --import tsx examples/echo-agent/start.ts 2>&1) echo "$out" echo "$out" | grep -q '\[tool call\] echo({"text":"ci smoke"})' echo "$out" | grep -q '\[tool result\] ECHO: CI SMOKE' # The JSONL backend (root ./.sessions, no cwd → _no-cwd bucket) writes a # per-run session log named main-session-.jsonl. Assert one exists. ls .sessions/_no-cwd/main-session-*.jsonl >/dev/null rm -rf .sessions