mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-07 07:55:47 +01:00
Reconcile Claude behavior with current native execution fixes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
commit
f006b3112b
154 files changed
+8949
-1197
No files matched your search
@@ -0,0 +1,139 @@
|
||||
name: CI work benchmark
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
paths:
|
||||
- .github/actions/setup-ci-dependencies/**
|
||||
- .github/workflows/ci-work-benchmark.yml
|
||||
- scripts/ci-dependency-cache.mjs
|
||||
- scripts/benchmark-ci-work.mjs
|
||||
- scripts/run-vitest-stable.mjs
|
||||
- server/src/__tests__/setup-supertest.ts
|
||||
- server/src/__tests__/workspace-runtime.test.ts
|
||||
- server/src/services/workspace-git-snapshot-streaming.test.ts
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ci-work-benchmark-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
paired:
|
||||
if: >-
|
||||
contains(github.event.pull_request.labels.*.name, 'profile-ci') &&
|
||||
(github.event.action != 'labeled' || github.event.label.name == 'profile-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
defaults:
|
||||
run:
|
||||
working-directory: candidate
|
||||
steps:
|
||||
- name: Checkout candidate
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: candidate
|
||||
fetch-depth: 2
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
|
||||
with:
|
||||
node-version: 24
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86
|
||||
with:
|
||||
version: 9.15.4
|
||||
run_install: false
|
||||
|
||||
- name: Start cold setup measurement
|
||||
run: echo "CI_COLD_STARTED=$(date +%s)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Qualify candidate dependency setup
|
||||
uses: ./candidate/.github/actions/setup-ci-dependencies
|
||||
with:
|
||||
working-directory: candidate
|
||||
restore-cache: 'false'
|
||||
|
||||
- name: Record cold setup and compute qualification keys
|
||||
id: qualification
|
||||
run: |
|
||||
echo "CI_COLD_SECONDS=$(($(date +%s) - CI_COLD_STARTED))" >> "$GITHUB_ENV"
|
||||
node scripts/ci-dependency-cache.mjs
|
||||
|
||||
- name: Save PR-scoped qualification dependencies
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
|
||||
with:
|
||||
key: ${{ steps.qualification.outputs.dependency-key }}
|
||||
path: ${{ steps.qualification.outputs.dependency-paths }}
|
||||
|
||||
- name: Save PR-scoped qualification builds
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
|
||||
with:
|
||||
key: ${{ steps.qualification.outputs.build-key }}
|
||||
path: ${{ steps.qualification.outputs.build-paths }}
|
||||
|
||||
- name: Remove materialized outputs before restore
|
||||
run: |
|
||||
node --input-type=module <<'NODE'
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { rmSync } from 'node:fs';
|
||||
const plan = JSON.parse(execFileSync(process.execPath, ['scripts/ci-dependency-cache.mjs'], {
|
||||
env: { ...process.env, GITHUB_OUTPUT: '' }, encoding: 'utf8',
|
||||
}));
|
||||
for (const directory of [...plan.dependencyPaths, 'packages/shared/dist', 'packages/plugins/sdk/dist']) {
|
||||
rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
NODE
|
||||
echo "CI_WARM_STARTED=$(date +%s)" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Qualify a real warm restore
|
||||
id: warm
|
||||
uses: ./candidate/.github/actions/setup-ci-dependencies
|
||||
with:
|
||||
working-directory: candidate
|
||||
|
||||
- name: Verify exact cache hits and record setup comparison
|
||||
env:
|
||||
DEPS_RESTORED: ${{ steps.warm.outputs.dependencies-restored }}
|
||||
BUILDS_RESTORED: ${{ steps.warm.outputs.builds-restored }}
|
||||
OUTPUT: ${{ runner.temp }}/ci-work-benchmark
|
||||
run: |
|
||||
test "$DEPS_RESTORED" = true
|
||||
test "$BUILDS_RESTORED" = true
|
||||
mkdir -p "$OUTPUT"
|
||||
warm_seconds=$(($(date +%s) - CI_WARM_STARTED))
|
||||
printf '{"coldSeconds":%s,"warmSeconds":%s}\n' "$CI_COLD_SECONDS" "$warm_seconds" > "$OUTPUT/dependency-setup.json"
|
||||
echo "Dependency setup: ${CI_COLD_SECONDS}s cold, ${warm_seconds}s with exact materialized restores." >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Prepare unchanged baseline
|
||||
run: |
|
||||
base_sha=$(git rev-parse HEAD^1)
|
||||
git worktree add --detach ../baseline "$base_sha"
|
||||
cd ../baseline
|
||||
if ! pnpm install --frozen-lockfile; then
|
||||
pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile
|
||||
pnpm install --frozen-lockfile
|
||||
fi
|
||||
pnpm run preflight:workspace-links
|
||||
pnpm --filter @paperclipai/plugin-sdk ensure-build-deps
|
||||
|
||||
- name: Compare complete commands and executed case inventories
|
||||
env:
|
||||
BENCHMARK_OUTPUT: ${{ runner.temp }}/ci-work-benchmark
|
||||
run: |
|
||||
node scripts/benchmark-ci-work.mjs ../baseline "$BENCHMARK_OUTPUT"
|
||||
cat "$BENCHMARK_OUTPUT/summary.md" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Retain timings, inventories and failure logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
|
||||
with:
|
||||
name: ci-work-benchmark
|
||||
path: ${{ runner.temp }}/ci-work-benchmark
|
||||
retention-days: 14
|
||||
@@ -186,13 +186,19 @@ jobs:
|
||||
|
||||
# Share the checked-in lockfile key with master. PR merge refs must not
|
||||
# save full copies of the store or evict the post-merge build caches.
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
|
||||
- name: Locate pnpm store
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
id: pnpm_store
|
||||
run: |
|
||||
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
echo "arch=$(node -p 'process.arch')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store (read only)
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.pnpm_store.outputs.path }}
|
||||
@@ -200,6 +206,7 @@ jobs:
|
||||
restore-keys: node-cache-${{ runner.os }}-${{ steps.pnpm_store.outputs.arch }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
# Manifest-changing and stacked PRs can hold a pnpm-lock.yaml that is
|
||||
# stale for the merge tree. Resolve it inline instead of waiting on a
|
||||
# policy-job artifact: that dependency put the policy job's queue and
|
||||
@@ -347,26 +354,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# The server suite is pinned to maxWorkers=1 (server/vitest.config.ts),
|
||||
# so it can only be parallelized across runners. Shard it to keep this
|
||||
# lane off the PR critical path. The suite has grown to ~3184s of
|
||||
# serial vitest wall time (mean of runs 35036001734 and 35024948947,
|
||||
# 2026-09-15): at five plain general-server shards with stale
|
||||
# recorded durations the worst shard ran 806s of tests while the
|
||||
# best ran 417s. This now uses the same shape as release-verify.yml:
|
||||
# the ~429s chat integration suite splits by collected test location
|
||||
# across three dedicated runners (~143s each), and the remaining
|
||||
# ~2755s levels across twelve duration-balanced shards at ~230s
|
||||
# each, in line with the other ~200-290s lanes.
|
||||
#
|
||||
# The balance only holds while the manifest is fresh. By 2026-10-10
|
||||
# the without-chat group had grown to 871 suites and ~5031s of serial
|
||||
# vitest time (mean of runs 38076844891 and 38076972444), 230 of
|
||||
# them unrecorded and weighted at the median, so the twelve shards
|
||||
# ran 313-554s of tests (server 2/12 at 618s was the slowest check
|
||||
# in the run). Re-recorded durations level them to ~415s each; the
|
||||
# shard count stays at twelve because Build and workspaces-b sit
|
||||
# above that already.
|
||||
# Keep twelve duration-balanced server shards. Each shard overlaps
|
||||
# at most two isolated fork workers; cases within a file stay serial.
|
||||
# The chat fixture keeps three independent database-backed shards.
|
||||
- group: general-server-without-chat
|
||||
group_label: server (1/12)
|
||||
shard_index: 0
|
||||
@@ -484,13 +474,19 @@ jobs:
|
||||
|
||||
# Share the checked-in lockfile key with master. PR merge refs must not
|
||||
# save full copies of the store or evict the post-merge build caches.
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
|
||||
- name: Locate pnpm store
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
id: pnpm_store
|
||||
run: |
|
||||
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
echo "arch=$(node -p 'process.arch')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store (read only)
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.pnpm_store.outputs.path }}
|
||||
@@ -498,6 +494,7 @@ jobs:
|
||||
restore-keys: node-cache-${{ runner.os }}-${{ steps.pnpm_store.outputs.arch }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
# Manifest-changing and stacked PRs can hold a pnpm-lock.yaml that is
|
||||
# stale for the merge tree. Resolve it inline instead of waiting on a
|
||||
# policy-job artifact: that dependency put the policy job's queue and
|
||||
@@ -558,6 +555,14 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
|
||||
|
||||
- name: Set up Docker Compose
|
||||
uses: docker/setup-compose-action@54042514f505b273907334ae2b9cdbb9a0213c1a # v2
|
||||
with:
|
||||
version: v5.5.1
|
||||
|
||||
- name: Verify Compose signing secret forwarding
|
||||
run: node --test scripts/docker-compose-secrets.test.mjs
|
||||
|
||||
# Same .dockerignore semantics as the real image builds: a
|
||||
# context-slimming change that strips a committed build input must
|
||||
# fail here, on the pull request, instead of failing every
|
||||
@@ -684,13 +689,19 @@ jobs:
|
||||
|
||||
# Share the checked-in lockfile key with master. PR merge refs must not
|
||||
# save full copies of the store or evict the post-merge build caches.
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
|
||||
- name: Locate pnpm store
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
id: pnpm_store
|
||||
run: |
|
||||
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
echo "arch=$(node -p 'process.arch')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store (read only)
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.pnpm_store.outputs.path }}
|
||||
@@ -698,6 +709,7 @@ jobs:
|
||||
restore-keys: node-cache-${{ runner.os }}-${{ steps.pnpm_store.outputs.arch }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
# Manifest-changing and stacked PRs can hold a pnpm-lock.yaml that is
|
||||
# stale for the merge tree. Resolve it inline instead of waiting on a
|
||||
# policy-job artifact: that dependency put the policy job's queue and
|
||||
@@ -885,13 +897,19 @@ jobs:
|
||||
|
||||
# Share the checked-in lockfile key with master. PR merge refs must not
|
||||
# save full copies of the store or evict the post-merge build caches.
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
|
||||
- name: Locate pnpm store
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
id: pnpm_store
|
||||
run: |
|
||||
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
echo "arch=$(node -p 'process.arch')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store (read only)
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.pnpm_store.outputs.path }}
|
||||
@@ -899,6 +917,7 @@ jobs:
|
||||
restore-keys: node-cache-${{ runner.os }}-${{ steps.pnpm_store.outputs.arch }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
# Manifest-changing and stacked PRs can hold a pnpm-lock.yaml that is
|
||||
# stale for the merge tree. Resolve it inline instead of waiting on a
|
||||
# policy-job artifact: that dependency put the policy job's queue and
|
||||
@@ -1160,13 +1179,19 @@ jobs:
|
||||
|
||||
# Share the checked-in lockfile key with master. PR merge refs must not
|
||||
# save full copies of the store or evict the post-merge build caches.
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
|
||||
- name: Locate pnpm store
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
id: pnpm_store
|
||||
run: |
|
||||
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
echo "arch=$(node -p 'process.arch')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store (read only)
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.pnpm_store.outputs.path }}
|
||||
@@ -1174,6 +1199,7 @@ jobs:
|
||||
restore-keys: node-cache-${{ runner.os }}-${{ steps.pnpm_store.outputs.arch }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
# Manifest-changing and stacked PRs can hold a pnpm-lock.yaml that is
|
||||
# stale for the merge tree. Resolve it inline instead of waiting on a
|
||||
# policy-job artifact: that dependency put the policy job's queue and
|
||||
@@ -1239,13 +1265,19 @@ jobs:
|
||||
|
||||
# Share the checked-in lockfile key with master. PR merge refs must not
|
||||
# save full copies of the store or evict the post-merge build caches.
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
|
||||
- name: Locate pnpm store
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
id: pnpm_store
|
||||
run: |
|
||||
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
echo "arch=$(node -p 'process.arch')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store (read only)
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.pnpm_store.outputs.path }}
|
||||
@@ -1253,6 +1285,7 @@ jobs:
|
||||
restore-keys: node-cache-${{ runner.os }}-${{ steps.pnpm_store.outputs.arch }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
# Manifest-changing and stacked PRs can hold a pnpm-lock.yaml that is
|
||||
# stale for the merge tree. Resolve it inline instead of waiting on a
|
||||
# policy-job artifact: that dependency put the policy job's queue and
|
||||
@@ -1486,13 +1519,19 @@ jobs:
|
||||
|
||||
# Share the checked-in lockfile key with master. PR merge refs must not
|
||||
# save full copies of the store or evict the post-merge build caches.
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
|
||||
- name: Locate pnpm store
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
id: pnpm_store
|
||||
run: |
|
||||
echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
echo "arch=$(node -p 'process.arch')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore pnpm store (read only)
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.pnpm_store.outputs.path }}
|
||||
@@ -1500,6 +1539,7 @@ jobs:
|
||||
restore-keys: node-cache-${{ runner.os }}-${{ steps.pnpm_store.outputs.arch }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
# Manifest-changing and stacked PRs can hold a pnpm-lock.yaml that is
|
||||
# stale for the merge tree. Resolve it inline instead of waiting on a
|
||||
# policy-job artifact: that dependency put the policy job's queue and
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
package-manager-cache: false
|
||||
|
||||
# rust-cache hashes its absolute cache paths into the entry's version,
|
||||
# and GitHub only serves an entry whose key and version both match. The
|
||||
@@ -102,7 +102,14 @@ jobs:
|
||||
- name: Validate release package manifest
|
||||
run: node ./scripts/release-package-map.mjs check
|
||||
|
||||
- name: Restore materialized dependencies and shared builds
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') != ''
|
||||
uses: ./.github/actions/setup-ci-dependencies
|
||||
with:
|
||||
save-cache: ${{ github.repository == 'paperclipai/paperclip' && github.event_name == 'push' && github.ref == 'refs/heads/master' && inputs.ref == github.sha }}
|
||||
|
||||
- name: Install dependencies
|
||||
if: hashFiles('.github/actions/setup-ci-dependencies/action.yml') == ''
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
|
||||
Reference in new issue
Block a user