chore(release): v2026.4.26 — verification fixes, roadmap wired, 100% … #623
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: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| outputs: | |
| code: ${{ steps.filter.outputs.code }} | |
| schemas: ${{ steps.filter.outputs.schemas }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| code: | |
| - 'packages/**' | |
| - 'pnpm-workspace.yaml' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'tsconfig*.json' | |
| - 'vitest.config.ts' | |
| - 'build.mjs' | |
| schemas: | |
| - 'packages/*/schemas/**' | |
| - 'packages/*/templates/**' | |
| biome: | |
| name: Lint & Format | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: biomejs/setup-biome@v2 | |
| with: | |
| version: '2.4.8' | |
| - run: biome ci . | |
| typecheck: | |
| name: Type Check | |
| needs: [biome, changes] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: '10.30.0' | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "store=$(pnpm store path --silent)" >> $GITHUB_OUTPUT | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.store }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - run: pnpm install --frozen-lockfile | |
| - name: Type check with project references | |
| run: pnpm run typecheck | |
| unit-tests: | |
| name: Unit Tests (${{ matrix.os }}, shard ${{ matrix.shard }}) | |
| needs: [biome, changes] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 10 | |
| strategy: | |
| matrix: | |
| os: ${{ fromJson(github.event_name == 'pull_request' && github.base_ref == 'main' && '["ubuntu-latest","macos-latest","windows-latest"]' || '["ubuntu-latest"]') }} | |
| shard: [1, 2] | |
| fail-fast: false | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: '10.30.0' | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: echo "store=$(pnpm store path --silent)" >> $GITHUB_OUTPUT | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.store }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - run: pnpm install --frozen-lockfile | |
| - name: Build packages (required for cross-package imports) | |
| run: pnpm run build | |
| - name: Run unit tests (shard ${{ matrix.shard }}/2) | |
| run: pnpm exec vitest run --shard=${{ matrix.shard }}/2 | |
| build-verify: | |
| name: Build & Verify (${{ matrix.os }}) | |
| needs: [biome, changes] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 10 | |
| strategy: | |
| matrix: | |
| os: ${{ fromJson(github.event_name == 'pull_request' && github.base_ref == 'main' && '["ubuntu-latest","macos-latest","windows-latest"]' || '["ubuntu-latest"]') }} | |
| fail-fast: false | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: '10.30.0' | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: echo "store=$(pnpm store path --silent)" >> $GITHUB_OUTPUT | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.store }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - run: pnpm install --frozen-lockfile | |
| # ────────────────────────────────────────────────────────────────── | |
| # COLD-STATE BUILD GATE | |
| # | |
| # Purpose: catch workspace-dependency-order regressions in build.mjs | |
| # at PR-time, never at release-time. | |
| # | |
| # Background: in v2026.4.8 the release workflow failed at the Build | |
| # step because caamp's tsup DTS resolver couldn't find @cleocode/cant | |
| # — caamp had grown a new import that depended on cant's .d.ts files | |
| # being present on disk before caamp built, but build.mjs's order had | |
| # caamp building first. The bug only reproduced from a fully clean | |
| # state (no dist, no tsbuildinfo) because local re-builds left stale | |
| # dist files behind that masked the gap. | |
| # | |
| # CI checkouts are already fresh, so this defensive `rm -rf` is | |
| # belt-and-braces — it guarantees the build runs from zero state | |
| # regardless of any future caching changes to actions/checkout or | |
| # actions/cache. If someone reorders build.mjs incorrectly again, | |
| # this step fails and blocks the merge. | |
| # ────────────────────────────────────────────────────────────────── | |
| - name: Cold-state cleanup (defense against build-order regressions) | |
| shell: bash | |
| run: | | |
| rm -rf packages/*/dist packages/*/tsconfig.tsbuildinfo | |
| echo "Cleaned dist and tsbuildinfo from all packages." | |
| - name: Build all packages from cold state (catches dep-order regressions) | |
| run: node build.mjs | |
| - name: Verify core dist exists | |
| shell: bash | |
| run: | | |
| test -f packages/core/dist/index.js || (echo "packages/core/dist/index.js missing" && exit 1) | |
| - name: Verify lafs dist exists | |
| shell: bash | |
| run: | | |
| test -f packages/lafs/dist/src/index.js || (echo "packages/lafs/dist/src/index.js missing" && exit 1) | |
| - name: Verify caamp dist exists | |
| shell: bash | |
| run: | | |
| test -f packages/caamp/dist/index.js || (echo "packages/caamp/dist/index.js missing" && exit 1) | |
| - name: Verify cant dist exists | |
| shell: bash | |
| run: | | |
| test -f packages/cant/dist/index.js || (echo "packages/cant/dist/index.js missing" && exit 1) | |
| - name: Verify runtime dist exists | |
| shell: bash | |
| run: | | |
| test -f packages/runtime/dist/index.js || (echo "packages/runtime/dist/index.js missing" && exit 1) | |
| - name: Verify CLI binary | |
| shell: bash | |
| run: node packages/cleo/dist/cli/index.js version | |
| - name: Verify MCP server starts | |
| if: matrix.os != 'windows-latest' | |
| shell: bash | |
| run: timeout 5 node packages/cleo/dist/mcp/index.js 2>&1 || true | |
| validate-json: | |
| name: Validate JSON Schemas | |
| needs: [biome, changes] | |
| if: needs.changes.outputs.schemas == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install jq | |
| run: sudo apt-get install -y jq | |
| - name: Validate JSON schema files | |
| run: | | |
| find packages -name '*.json' -path '*/schemas/*' | while read file; do | |
| echo "Validating: $file" | |
| jq empty "$file" | |
| done | |
| install-test: | |
| name: Install Test | |
| needs: [biome, changes] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: '10.30.0' | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "store=$(pnpm store path --silent)" >> $GITHUB_OUTPUT | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.store }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install dependencies and build | |
| run: pnpm install --frozen-lockfile && node build.mjs | |
| - name: Test global link (simulates global install) | |
| run: | | |
| cd packages/cleo && pnpm link --global && cd ../.. | |
| cleo version | |
| cleo --help | |
| - name: Test MCP server flag | |
| run: timeout 5 cleo --mcp-server 2>&1 || true | |
| - name: Test project initialization | |
| run: | | |
| mkdir -p /tmp/test-project | |
| cd /tmp/test-project | |
| cleo init test-project | |
| ls -la .cleo/ | |
| - name: Cleanup | |
| if: always() | |
| run: pnpm unlink --global @cleocode/cleo | |
| forge-ts-check: | |
| name: Documentation Coverage (forge-ts) | |
| needs: [biome, changes] | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| run: echo "store=$(pnpm store path --silent)" >> $GITHUB_OUTPUT | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.store }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - run: pnpm install --frozen-lockfile | |
| - name: Check TSDoc coverage | |
| run: pnpm exec forge-ts check --human || true | |
| - name: Build documentation | |
| run: pnpm exec forge-ts build || true |