feat: implement Rust-only Zeroshot v2 MVP #2490
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 | |
| # Codecov setup: | |
| # 1. Go to https://codecov.io and sign in with GitHub | |
| # 2. Add the covibes/zeroshot repository | |
| # 3. Get the upload token from Settings > General | |
| # 4. Add it as a GitHub secret: Settings > Secrets and variables > Actions > New repository secret | |
| # Name: CODECOV_TOKEN | |
| # Value: <your-token-from-codecov> | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| merge_group: | |
| types: [checks_requested] | |
| workflow_dispatch: | |
| inputs: | |
| include_docker_e2e: | |
| description: Run the non-hermetic live-provider Docker E2E job | |
| required: false | |
| type: boolean | |
| default: false | |
| jobs: | |
| # Fast quality gates - must pass before anything else | |
| check: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 | |
| with: | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Setup Rust 1.97.0 | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: 1.97.0 | |
| components: clippy,rustfmt | |
| - name: Install Opcore Rust metrics analyzer | |
| run: npm run opcore:install:rust-metrics | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build Opcore graph | |
| run: npm run opcore:graph:build | |
| - name: Opcore introduced-change gate | |
| env: | |
| CARGO_BUILD_JOBS: 1 | |
| OPCORE_BASE: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before || 'HEAD^' }} | |
| run: npm run opcore:check -- --base "$OPCORE_BASE" | |
| - name: Check generated Cluster Protocol artifacts | |
| run: npm run protocol:check | |
| - name: Check Rust formatting | |
| run: cargo fmt --all -- --check | |
| - name: Lint Rust | |
| run: cargo clippy --workspace --all-targets -- -D warnings | |
| - name: Test Rust workspace | |
| run: cargo test --workspace | |
| - name: Security audit | |
| run: npm run audit:production | |
| continue-on-error: false | |
| - name: Lint | |
| run: npm run lint | |
| - name: Duplicate code check | |
| run: npm run dupcheck | |
| - name: Type check | |
| run: npm run typecheck | |
| - name: Check provider CLI helper | |
| run: npm run check:agent-cli-provider:ci | |
| - name: Hosted private CLI and target tests | |
| run: npm run test:hosted | |
| timeout-minutes: 5 | |
| - name: Unit tests with coverage (fast) | |
| run: npm run test:coverage | |
| timeout-minutes: 3 | |
| - name: E2E tests (fake provider) | |
| run: npm run test:e2e | |
| timeout-minutes: 5 | |
| - name: Integration tests (slow) | |
| run: npm run test:slow | |
| timeout-minutes: 15 | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./coverage/lcov.info | |
| flags: unittests | |
| name: codecov-zeroshot | |
| fail_ci_if_error: false | |
| continue-on-error: true | |
| # Tier 2: worktree/container isolation e2e (real Docker). Non-blocking and | |
| # opt-in via workflow_dispatch - the Docker sub-tests in `check`'s | |
| # "Integration tests (slow)" step self-skip under CI (this is what that | |
| # step's Docker coverage was previously silently missing). | |
| e2e-docker: | |
| if: github.event_name == 'workflow_dispatch' && inputs.include_docker_e2e | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 | |
| with: | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Link CLI | |
| run: npm link | |
| - name: Docker isolation e2e | |
| run: npm run test:e2e:docker | |
| timeout-minutes: 15 | |
| hosted-oecp-relevance: | |
| name: Hosted OECP image relevance | |
| runs-on: ubuntu-latest | |
| outputs: | |
| relevant: ${{ steps.relevance.outputs.relevant }} | |
| steps: | |
| - name: Checkout candidate | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || github.sha }} | |
| - name: Determine hosted image relevance | |
| id: relevance | |
| run: node scripts/hosted-oecp-ci-relevance.js | |
| hosted-oecp-image: | |
| name: Hosted OECP private image | |
| needs: hosted-oecp-relevance | |
| if: needs.hosted-oecp-relevance.outputs.relevant == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| env: | |
| HOSTED_OECP_IMAGE: zeroshot-oecp:ci-${{ github.run_id }}-${{ github.run_attempt }} | |
| TRIVY_IMAGE: docker.io/aquasec/trivy:0.73.0@sha256:7cced7cae583819fc7806d4cbc0dbbc7cad18b99f7d3e235192e6da8c091045c | |
| steps: | |
| - name: Checkout candidate | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 | |
| with: | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build agent CLI provider | |
| run: npm run build:agent-cli-provider | |
| - name: Build hosted OECP image | |
| run: node scripts/hosted-oecp-image.js build "$HOSTED_OECP_IMAGE" | |
| - name: Inspect hosted OECP image | |
| run: node scripts/hosted-oecp-image.js inspect "$HOSTED_OECP_IMAGE" | |
| - name: Smoke hosted OECP image | |
| run: node scripts/hosted-oecp-image.js smoke "$HOSTED_OECP_IMAGE" | |
| - name: Record Trivy and vulnerability database identity | |
| shell: bash | |
| env: | |
| TRIVY_CACHE_DIR: ${{ runner.temp }}/trivy-cache | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$TRIVY_CACHE_DIR" | |
| echo "Trivy scanner image: $TRIVY_IMAGE" | |
| docker run --rm \ | |
| --volume "$TRIVY_CACHE_DIR:/root/.cache/trivy" \ | |
| "$TRIVY_IMAGE" image --cache-dir /root/.cache/trivy --download-db-only | |
| docker run --rm \ | |
| --volume "$TRIVY_CACHE_DIR:/root/.cache/trivy" \ | |
| "$TRIVY_IMAGE" version --cache-dir /root/.cache/trivy --format json | |
| - name: Scan hosted OECP npm lockfile vulnerabilities | |
| shell: bash | |
| env: | |
| TRIVY_CACHE_DIR: ${{ runner.temp }}/trivy-cache | |
| run: | | |
| set -euo pipefail | |
| echo "Trivy filesystem scan target: docker/zeroshot-oecp" | |
| echo "Trivy vulnerability scope: library lockfiles, fixed HIGH/CRITICAL vulnerabilities" | |
| docker run --rm \ | |
| --volume "$TRIVY_CACHE_DIR:/root/.cache/trivy" \ | |
| --volume "$GITHUB_WORKSPACE:/workspace:ro" \ | |
| --workdir /workspace \ | |
| "$TRIVY_IMAGE" fs \ | |
| --cache-dir /root/.cache/trivy \ | |
| --scanners vuln \ | |
| --pkg-types library \ | |
| --severity HIGH,CRITICAL \ | |
| --ignore-unfixed \ | |
| --exit-code 1 \ | |
| --skip-db-update \ | |
| --offline-scan \ | |
| --no-progress \ | |
| docker/zeroshot-oecp | |
| # Cross-platform/version compatibility (main only) | |
| # NOTE: macOS runners cost 10x Linux minutes - keep this scoped to main. | |
| install-matrix: | |
| needs: check | |
| if: | | |
| always() && | |
| needs.check.result == 'success' && | |
| ( | |
| github.ref == 'refs/heads/main' || | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'pull_request' && github.base_ref == 'main') || | |
| (github.event_name == 'merge_group' && ( | |
| github.event.merge_group.base_ref == 'main' || | |
| github.event.merge_group.base_ref == 'refs/heads/main' || | |
| startsWith(github.ref, 'refs/heads/gh-readonly-queue/main/') | |
| )) | |
| ) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| node: 22 | |
| - os: macos-latest | |
| node: 22 | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js ${{ matrix.node }} | |
| uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Link CLI | |
| run: npm link | |
| - name: CLI smoke test | |
| run: | | |
| zeroshot --help | |
| zeroshot --version | |
| zeroshot list | |
| - name: Test process-metrics (platform-specific) | |
| run: | | |
| node -e " | |
| (async () => { | |
| const { getProcessMetrics, formatMetrics, getStateIcon, isPlatformSupported, getPlatformInfo } = require('./src/process-metrics'); | |
| const os = require('os'); | |
| console.log('Platform:', os.platform()); | |
| console.log('Supported:', isPlatformSupported()); | |
| console.log('Platform info:', JSON.stringify(getPlatformInfo())); | |
| // Test metrics collection | |
| const metrics = await getProcessMetrics(process.pid, { samplePeriodMs: 100 }); | |
| console.log('Metrics:', JSON.stringify(metrics, null, 2)); | |
| // Validate required fields | |
| if (!metrics.exists) throw new Error('Process should exist'); | |
| if (typeof metrics.cpuPercent !== 'number') throw new Error('cpuPercent must be number'); | |
| if (typeof metrics.memoryMB !== 'number') throw new Error('memoryMB must be number'); | |
| if (!metrics.state) throw new Error('state must be set'); | |
| console.log('Formatted:', formatMetrics(metrics)); | |
| console.log('State icon:', getStateIcon(metrics.state, metrics.network?.hasActivity)); | |
| console.log('✓ Process metrics test passed on', os.platform()); | |
| })().catch(e => { console.error(e); process.exit(1); }); | |
| " | |
| - name: Test status-footer | |
| run: | | |
| node -e " | |
| const { StatusFooter } = require('./src/status-footer'); | |
| const footer = new StatusFooter({ enabled: false }); | |
| footer.setCluster('test-cluster'); | |
| footer.setClusterState('running'); | |
| footer.updateAgent({ id: 'worker', state: 'executing_task', pid: process.pid, iteration: 1 }); | |
| const statusLine = footer.buildStatusLine(80); | |
| if (!statusLine.includes('running')) throw new Error('Status line missing state'); | |
| if (!statusLine.includes('1/1')) throw new Error('Status line missing agent count'); | |
| footer.stop(); | |
| console.log('✓ StatusFooter test passed'); | |
| " | |
| release-preflight: | |
| name: Release preflight | |
| if: | | |
| (github.event_name == 'pull_request' && github.base_ref == 'main') || | |
| (github.event_name == 'merge_group' && ( | |
| github.event.merge_group.base_ref == 'main' || | |
| github.event.merge_group.base_ref == 'refs/heads/main' || | |
| startsWith(github.ref, 'refs/heads/gh-readonly-queue/main/') | |
| )) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 | |
| with: | |
| node-version: 22 | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Validate release contract | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: npm run release:preflight | |
| required: | |
| name: required | |
| if: ${{ always() }} | |
| needs: | |
| - check | |
| - install-matrix | |
| - release-preflight | |
| - hosted-oecp-relevance | |
| - hosted-oecp-image | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Require every applicable CI gate | |
| env: | |
| CHECK_RESULT: ${{ needs.check.result }} | |
| INSTALL_MATRIX_RESULT: ${{ needs.install-matrix.result }} | |
| RELEASE_PREFLIGHT_RESULT: ${{ needs.release-preflight.result }} | |
| HOSTED_OECP_RELEVANCE_RESULT: ${{ needs.hosted-oecp-relevance.result }} | |
| HOSTED_OECP_RELEVANT: ${{ needs.hosted-oecp-relevance.outputs.relevant }} | |
| HOSTED_OECP_IMAGE_RESULT: ${{ needs.hosted-oecp-image.result }} | |
| run: | | |
| if [[ "$CHECK_RESULT" != "success" ]]; then | |
| echo "::error::check concluded with $CHECK_RESULT" | |
| exit 1 | |
| fi | |
| if [[ "$INSTALL_MATRIX_RESULT" != "success" ]]; then | |
| echo "::error::install-matrix concluded with $INSTALL_MATRIX_RESULT" | |
| exit 1 | |
| fi | |
| if [[ "$RELEASE_PREFLIGHT_RESULT" != "success" && "$RELEASE_PREFLIGHT_RESULT" != "skipped" ]]; then | |
| echo "::error::release-preflight concluded with $RELEASE_PREFLIGHT_RESULT" | |
| exit 1 | |
| fi | |
| if [[ "$HOSTED_OECP_RELEVANCE_RESULT" != "success" ]]; then | |
| echo "::error::hosted-oecp-relevance concluded with $HOSTED_OECP_RELEVANCE_RESULT" | |
| exit 1 | |
| fi | |
| if [[ "$HOSTED_OECP_RELEVANT" != "true" && "$HOSTED_OECP_RELEVANT" != "false" ]]; then | |
| echo "::error::hosted-oecp-relevance emitted invalid relevance: $HOSTED_OECP_RELEVANT" | |
| exit 1 | |
| fi | |
| if [[ "$HOSTED_OECP_RELEVANT" == "true" && "$HOSTED_OECP_IMAGE_RESULT" != "success" ]]; then | |
| echo "::error::hosted-oecp-image concluded with $HOSTED_OECP_IMAGE_RESULT for relevant changes" | |
| exit 1 | |
| fi | |
| if [[ "$HOSTED_OECP_RELEVANT" == "false" && "$HOSTED_OECP_IMAGE_RESULT" != "skipped" ]]; then | |
| echo "::error::hosted-oecp-image concluded with $HOSTED_OECP_IMAGE_RESULT for irrelevant changes" | |
| exit 1 | |
| fi |