- Switch STORE_PATH from $GITHUB_ENV to $GITHUB_OUTPUT (step-scoped,
per GitHub Actions security hardening guide)
- Narrow hashFiles glob from '**/pnpm-lock.yaml' to 'pnpm-lock.yaml'
(root-only, avoids spurious cache busts from docs/examples lockfiles)
- Reword comment to show ABI mismatch is symmetric across all versions
- Document that actions/cache is equally fork-safe (GitHub platform guarantee)
pnpm rebuild better-sqlite3 was a no-op — pnpm treats the arg as a
workspace package name and exits silently when none matches.
Root fix: replace setup-node's built-in cache: "pnpm" (whose key omits
the Node.js version) with a manual actions/cache step that includes
matrix.node-version in the key. Each Node version gets its own pnpm
store, so the ABI-137 better-sqlite3 binary cached from a Node 24 job
can no longer be served to Node 20 (needs ABI 115) or Node 22 (needs
ABI 127) jobs.
The pnpm store cache key used by setup-node does not include the Node.js
version, so a cache entry written by a Node 24 job contains an ABI-137
better-sqlite3 binary. When Node 20 (ABI 115) or Node 22 (ABI 127) jobs
restore from the same cache key they get the wrong binary and all
sqlite-runner tests fail with "Module did not self-register".
Adding `pnpm rebuild better-sqlite3` after install re-runs prebuild-install
for the active Node version, downloading the correct prebuilt binary and
overwriting whatever ABI was in the restored cache.
Root cause first appeared after the setup-node v4→v6 and pnpm/action-setup
v4→v6 bumps (May 15, PRs #4857/#4858) reset the cold cache, allowing a
Node 24 binary to poison the shared store entry.
Comprehensive CI/CD security hardening pass over all 33 workflows.
Action pinning
- Every `uses:` is now pinned to a 40-char commit SHA with a `# vX.Y.Z`
comment alongside (167 occurrences resolved). Tag-style refs like `@v4`
are mutable and have been used in past supply-chain attacks (e.g.
tj-actions/changed-files in March 2025) to repoint widely-used actions
to malicious commits.
- Removed redundant `version: "10.13.1"` hardcodes from `pnpm/action-setup`
call sites so the action inherits from package.json `packageManager`
(one source of truth).
Automated maintenance
- Added `.github/dependabot.yml` for the `github-actions` ecosystem so
SHA pins stay current. Without this, pins go stale fast and new
upstream advisories never reach us. Minor/patch bumps are grouped;
major bumps stay separate so they get a real review.
Static analysis
- Added `.github/zizmor.yml` configuration and
`.github/workflows/security_zizmor.yml` (blocking on PR, runs on push
to main, weekly schedule for advisory drift). zizmor catches the
well-known classes of Actions footguns: template injection from
untrusted input, dangerous triggers, unpinned uses, excessive token
scopes, secret exfil patterns.
- All 28 high-severity and 54 medium-severity findings from the baseline
scan are remediated. Each suppression in zizmor.yml carries a
per-finding justification comment so future maintainers can audit the
trust assumption.
Workflow hardening (from zizmor + manual audit)
- Added `persist-credentials: false` to every `actions/checkout` except
the 7 workflows that legitimately push back to the repo via the
workflow token (release tagging, auto-formatting, docs-sync, registry
updates). Each retained credential persistence carries a
`persist-credentials required: ...` comment explaining the call site.
- Routed every attacker-controllable expansion (`github.head_ref`,
`github.event.pull_request.head.repo.full_name`, `inputs.*`,
step outputs) through `env:` and referenced as quoted shell variables.
Eliminates 17 template-injection vectors in fork-PR-reachable
workflows.
- Added per-job `permissions:` blocks across 14 workflows; demoted
broad workflow-level `id-token: write` to the specific Depot-runner
jobs that need it; narrowed `pull-requests: write` /
`actions: write` to the jobs that actually call those APIs.
Audit-driven fixes
- `publish-release.yml` build job: dropped `token:` and added
`persist-credentials: false`. The subsequent `Upload workspace` step
was packing `.git/config` (with the persisted GITHUB_TOKEN) into a
1-day-retention artifact downloadable by anyone with `actions:read`.
- `auto_merge_showcases.yml`: team-membership check now authorizes on
the PR AUTHOR (`pull_request.user.login`), never `context.actor` —
the actor is whoever triggered the latest event, so a team member
synchronizing or reopening an outsider's PR would otherwise
green-light auto-merge of code they didn't author.
- `static_quality.yml`: pinned ruff to a specific version so a
compromised release can't land on the next PR run with the
persisted-credentials write token in the format job.
- `showcase_capture-previews.yml`: switched the args-string construction
to a bash array so a slug or demo value containing whitespace or shell
metacharacters stays a single argument rather than being re-tokenized
by the shell.
## CI hygiene fixes
Four direct-fix items surfaced during the 2026-04-16 QA/E2E blitz,
bundled as one PR with one commit per fix:
1. **`test_unit.yml` paths-ignore** — add `showcase/**` +
`sdk-python/**` (prevents spurious TS unit matrix runs on showcase-only
or sdk-python-only PRs). `sdk-python-old/**` was mentioned in the plan
but doesn't exist on main; only the two existing dirs are added.
2. **`test_doc-examples.yml`** — scope PR trigger to `branches: [main]`
(matches convention of other workflows).
3. **`e2e_dojo.yml`** — symmetric `.changeset` path filter on push+PR
(was asymmetric — already in the `dorny/paths-filter` step's `ts:` list,
so this aligns the trigger).
4. **`starter-smoke.yml`** — rename internal job id (`starter-smoke` →
`smoke-starter`) and artifact name pattern (cosmetic; matches the new
`test_<layer>-<target>` / `smoke-<layer>` naming convention; no external
consumers).
### Fix skipped
**`showcase_smoke-monitor.yml` slug normalization** (item #10 in the
Notion page) — inspecting the file, it already uses the
`showcase/packages/` slug convention (`ms-agent-python`,
`ms-agent-dotnet`, `strands`). The apparent inconsistency is actually in
`starter-smoke.yml`, whose matrix keys must stay as
`ms-agent-framework-*` / `strands-python` because they are literal
directory names in `examples/integrations/` (used as `working-directory:
examples/integrations/${{ matrix.starter }}`). So there is no actionable
change here — the two files use different slug conventions *by
necessity*, because they target different directories (deployed
`showcase/packages/*` vs. local `examples/integrations/*`). Flagging for
the author of the blitz notes in case the actual concern was something
else.
Refs: [Bugs Found During
Blitz](https://www.notion.so/3443aa381852812fb595c5118dd68818) items #3,
#8, #9, #12.
Depot Startup plan (unlimited minutes) resolves the vitest birpc
onTaskUpdate timeouts on subprocess-heavy suites that standard
ubuntu-latest runners were amplifying. Extends the pattern from
PR #4018 (showcase_validate.yml, showcase_drift-report.yml).
Also adds the id-token: write permission required for Depot OIDC
auth, alongside contents: read for least-privilege defaults.
Showcase-only and sdk-python-only PRs were triggering the full TS unit matrix
(3 Node versions x full monorepo). Neither directory affects TS unit tests.
Now paths-ignore matches the spirit of the existing 'examples/**' exclusion.
- Add tests for bumpPackages: verifies workspace:* protocol is
preserved and exact version deps are updated
- Add vitest config for scripts/release/
- Add release script test step to test_unit.yml so these run on
every PR and push to main
- Add package.json exports for v2/{express,hono,node} subpaths
- Add elysia devDependency and tsdown entry points
- Exclude bun integration tests from vitest config
- Update CI workflows to include runtime-servers test job
- Add runtime-server-adapter docs page
- Add changeset for the fetch-based runtime feature
- Add fail-fast: false to unit test matrix strategies so one Node
version failing doesn't cancel all other matrix jobs
- Add retry: 2 to runtime-client-gql vitest config (Nx confirmed
this package's tests as flaky during parallel execution)
- Replace fragile setTimeout waits in middleware express tests with
vi.waitFor() polling, which is resilient to CI timing variance
https://claude.ai/code/session_01W2Kb2HXsjZett3HdqM9zay
Flatten all packages from packages/v1/* and packages/v2/* into packages/* —
every package now lives directly under the @copilotkit/ scope with no v1/v2
subdirectories.
- Move all v1 packages (react-core, react-ui, runtime, shared, etc.) from
packages/v1/* to packages/*
- Absorb v2 react code into packages/react-core/src/v2/ (exported via /v2 subpath)
- Absorb v2 agent code into packages/runtime/src/agent/ (exported via /v2 subpath)
- Move v2 packages (core, angular, demo-agents, etc.) to packages/*
- Replace all @copilotkitnext/* imports with @copilotkit/* equivalents
- Keep @copilotkitnext/angular as the sole exception (angular remains on next)
- Update CI workflows, renovate config, release scripts for flat structure
- No public API surface changes — all exports fields are preserved
Co-authored-by: Alem Tuzlak <t.zlak@hotmail.com>
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>