Repository navigation
feat(chat): exact-match cache for DEVIN_CONNECT path (stream + non-stream) #865
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: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| branches: [master] | |
| # Manual mutation sweep (Actions -> CI -> Run workflow). Deliberately NOT on the PR path: | |
| # each mutation re-runs its spec's whole test suite, so all 44 specs is hours, and a gate | |
| # nobody can afford to run is worse than no gate — that is the warning in the specs job's | |
| # own comment. Until 2026-09-13 nothing in this repo ever executed `npm run mutate`. | |
| workflow_dispatch: | |
| inputs: | |
| specs: | |
| description: 'Spec files to run (space separated). Blank = every test/mutations/*.json' | |
| required: false | |
| default: '' | |
| shard: | |
| description: 'Zero-based shard i/N (0/1 = all selected specs)' | |
| required: false | |
| default: '0/1' | |
| jobs: | |
| mutations: | |
| name: Mutation specs (manual) | |
| if: github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 350 | |
| steps: | |
| # fetch-depth 0: the mutation harness materializes its workspace with | |
| # `git clone --local` and reads history objects, so a shallow checkout fails it. | |
| # Measured 2026-09-13: with the default depth 1 the sweep died on the FIRST spec | |
| # with exit 2 and produced no verdict lines at all. | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node 24 | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '24' | |
| - name: Install dependencies | |
| run: npm ci | |
| # PR/push jobs intentionally do not execute mutation specs: this is a cost boundary. | |
| # Optional parallel form (replace the input shard with matrix.shard in Sweep): | |
| # strategy: | |
| # fail-fast: false | |
| # matrix: | |
| # shard: ['0/4', '1/4', '2/4', '3/4'] | |
| # Put strategy at the mutations job level, not inside steps. Each job has its own | |
| # checkout, manifest and artifact; never run two shards in one owner worktree. | |
| - name: Sweep | |
| env: | |
| REQUESTED_SPECS: ${{ inputs.specs }} | |
| REQUESTED_SHARD: ${{ inputs.shard }} | |
| run: | | |
| args=(--shard "${REQUESTED_SHARD:-0/1}" --report-dir "$RUNNER_TEMP/mutation-report") | |
| if [ -n "$REQUESTED_SPECS" ]; then args+=(--specs "$REQUESTED_SPECS"); fi | |
| node scripts/mutation-sweep.mjs "${args[@]}" | |
| - name: Assert verdict coverage | |
| if: always() | |
| run: node scripts/mutation-sweep.mjs --verify "$RUNNER_TEMP/mutation-report" | |
| - name: Publish mutation evidence | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: mutation-report-${{ github.run_id }}-${{ strategy.job-index || 0 }} | |
| path: ${{ runner.temp }}/mutation-report/ | |
| if-no-files-found: error | |
| syntax: | |
| name: Node syntax check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Setup Node 24 | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '24' | |
| - name: Check JS syntax for all src files | |
| run: | | |
| find src -name '*.js' -type f -print0 | xargs -0 -n1 node --check | |
| - name: Validate package.json | |
| run: node -e "JSON.parse(require('fs').readFileSync('package.json'))" | |
| - name: Validate JSON configs | |
| run: | | |
| for f in $(find . -maxdepth 2 -name '*.json' -not -path './node_modules/*' -not -path './.git/*'); do | |
| echo "Checking $f" | |
| node -e "JSON.parse(require('fs').readFileSync('$f'))" | |
| done | |
| # Mutation-spec consistency. Three baseline/anchor drifts reached master in one week | |
| # (#241's anchors, retry-rescue 82/87 -> 88, reasoning-continuity 289 -> 300) and the | |
| # test suite saw none of them: it does not run specs. The anchor/shape checks read files | |
| # only and cost ~0.3s, so they are mandatory rather than a local habit to remember. | |
| # | |
| # Baseline VALUE drift is measured here too, by EXECUTING the covered specs and reading | |
| # the runner's events (scripts/spec-baseline-check.mjs) — never by scraping display text | |
| # and never by rewriting a pin. A changed diff measures only the affected specs; a | |
| # synchronised checkout measures all of them, which costs ~100s for 82 specs. The check | |
| # used to run only in the local gate, so a wrong pin reached CI green. | |
| specs: | |
| name: Mutation spec consistency | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| # Change-scoped selection diffs against origin/master, so the history has to be | |
| # here; a shallow clone would silently widen the selection to every spec. | |
| fetch-depth: 0 | |
| - name: Setup Node 24 | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '24' | |
| - name: Anchors unique, specs well-formed | |
| run: node scripts/spec-static-check.mjs | |
| - name: Measured baselines match, from executed evidence | |
| run: node scripts/spec-baseline-check.mjs | |
| - name: Secret scan (includes test/ — half of a typical diff lives there) | |
| run: node scripts/secret-scan.mjs | |
| wire-identity: | |
| name: Default-path request bytes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| # The comparison base is a tag, so the tags have to be here. | |
| fetch-depth: 0 | |
| - name: Setup Node 24 | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '24' | |
| - name: Select the release baseline (strict ancestor, recorded) | |
| # `git describe --tags --abbrev=0` answers "nearest reachable tag", and at a | |
| # tagged HEAD that is HEAD's own tag: a rerun of a release commit compared | |
| # the tree with itself. scripts/wire-base.mjs picks the newest ANNOTATED | |
| # release tag that is a strict ancestor of the tested commit — no | |
| # lightweight fallback — so a rerun at a tag repeats the same comparison | |
| # against the PREVIOUS release, the recorded SHAs never change, and a | |
| # repository without such a tag fails closed (exit 3). | |
| run: | | |
| node scripts/wire-base.mjs | |
| BASE_TAG=$(node scripts/wire-base.mjs --field tag) | |
| BASE_SHA=$(node scripts/wire-base.mjs --field sha) | |
| HEAD_SHA=$(git rev-parse HEAD) | |
| if [ "$BASE_SHA" = "$HEAD_SHA" ]; then | |
| echo "::error::refusing to compare $HEAD_SHA against itself" | |
| exit 1 | |
| fi | |
| git worktree add --detach "$RUNNER_TEMP/wire-base" "$BASE_SHA" | |
| echo "comparing $HEAD_SHA against $BASE_SHA ($BASE_TAG)" | |
| echo "WIRE_BASE_TREE=$RUNNER_TEMP/wire-base" >> "$GITHUB_ENV" | |
| echo "WIRE_BASE_SHA=$BASE_SHA" >> "$GITHUB_ENV" | |
| echo "WIRE_BASE_TAG=$BASE_TAG" >> "$GITHUB_ENV" | |
| echo "WIRE_BASE_REQUIRED=1" >> "$GITHUB_ENV" | |
| - name: Same bytes as the last release, or this fails | |
| run: node --import ./test/setup-env.mjs --test test/wire-byte-identity.test.js | |
| test: | |
| name: Unit tests shard ${{ matrix.shard }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [0, 1, 2, 3] | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Setup Node 24 | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| # image-resize.test.js imports jimp (the sole runtime dependency), so the | |
| # runner needs node_modules — without this step every shard that touches | |
| # a jimp-importing test fails with ERR_MODULE_NOT_FOUND. | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run tests | |
| run: npm run test:shard -- ${{ matrix.shard }} 4 |